为何ASP Time输出存入SQL Server时需加N前缀?AM/PM显示为??问题咨询
这是个很容易被忽略的细节问题,我来帮你理清楚背后的原因:
核心原因:Unicode vs 非Unicode的转换陷阱
SQL Server里的字符串常量默认是**非Unicode(单字节)**类型的(对应VARCHAR),而你的mtime字段是NVARCHAR(50)(Unicode双字节类型)。当你插入不带N前缀的字符串时,SQL Server会做两步操作:
- 先把传入的字符串按照数据库默认排序规则的代码页转换成非Unicode编码;
- 再把这个非Unicode编码转换成Unicode编码存储到
NVARCHAR字段里。
问题就出在第一步转换上:如果你的数据库默认排序规则对应的代码页(比如某些东亚语言的代码页)和ASP输出的Time字符串的编码不匹配,哪怕是英文字符的AM/PM,也可能在转换过程中出现编码丢失,最终变成??。
举个例子:如果数据库用的是中文简体的排序规则(比如Chinese_PRC_CI_AS),它的代码页是GBK。虽然GBK包含ASCII字符,但如果ASP输出的Time字符串是UTF-8编码的,在转换成GBK的过程中,可能因为隐式编码转换的问题导致字符损坏——哪怕看起来是普通的英文字母。
解决办法
1. 给时间字符串加上N前缀
最简单的修改就是给t变量也加上N前缀,让SQL Server直接把它当作Unicode常量处理,跳过不必要的编码转换:
<% t = Time sql = "insert into notes (body,mtime) values (N'" & body & "',N'" & t & "')" Con.Execute sql %>
2. 更推荐:使用参数化查询
字符串拼接不仅容易出现编码问题,还存在SQL注入的风险。用参数化查询可以彻底解决这两个问题,而且不需要手动加N前缀——ADO会自动处理Unicode类型的参数:
<% Set cmd = Server.CreateObject("ADODB.Command") cmd.ActiveConnection = Con cmd.CommandText = "INSERT INTO notes (body, mtime) VALUES (@body, @mtime)" ' 定义参数:adVarWChar代表Unicode字符串,adParamInput是输入参数 cmd.Parameters.Append cmd.CreateParameter("@body", 202, 1, 50, body) cmd.Parameters.Append cmd.CreateParameter("@mtime", 202, 1, 50, Time) cmd.Execute Set cmd = Nothing %>
(注:如果你的代码里没有包含ADOVBS.inc文件,需要手动定义常量:adVarWChar=202,adParamInput=1)
总结
哪怕Time输出的是英文字符串,只要目标字段是NVARCHAR,就应该用N前缀或者参数化查询来传递字符串。这样可以避免任何编码转换带来的乱码问题,同时保证数据的安全性。
内容的提问来源于stack exchange,提问作者Ali Sheikhpour

