我的 VARCHAR(MAX) 字段的上限为 4000;什么给了?
我的一个数据库中有一个表,它是电子邮件队列。发送到某些地址的电子邮件会累积到一封电子邮件中,这是由存储过程完成的。在存储过程中,我有一个表变量,用于构建电子邮件的累积正文,然后循环发送每封电子邮件。在我的表 var 中,我将正文列定义为 VARCHAR(MAX) ,因为给定的电子邮件地址当前可能累积任意数量的电子邮件。看起来,即使我的列被定义为 VARCHAR(MAX) ,它的行为就好像它是 VARCHAR(4000) 并截断进入其中的数据,尽管它不会抛出任何异常,它只是在 4000 个字符后默默地停止连接任何更多数据。
MERGE 语句将累积的电子邮件正文构建到 @EMAILS.BODY 中,该字段将自身截断为 4000 个字符。
编辑
我已经更新了我的 MERGE 语句,试图将整个分配的字符串转换为 VARCHAR(MAX),但它仍然默默地将自身截断为 4000 个字符...这是我的新 MERGE:
MERGE @EMAILS AS DST
USING (SELECT * FROM @ROWS WHERE ROWID = @CURRID) AS SRC
ON SRC.ADDRESS = DST.ADDRESS
WHEN MATCHED THEN
UPDATE SET
DST.ALLIDS = DST.ALLIDS + ', ' + CONVERT(VARCHAR,ROWID),
DST.BODY = DST.BODY +
CONVERT(VARCHAR(MAX),
'<i>'+CONVERT(VARCHAR,SRC.DATED,101)+
' '+CONVERT(VARCHAR,SRC.DATED,8)+
':</i> <b>'+SRC.SUBJECT+'</b>'+CHAR(13)+
SRC.BODY+' (Message ID '+
CONVERT(VARCHAR,SRC.ROWID)+')'+
CHAR(13)+CHAR(13)
)
WHEN NOT MATCHED BY TARGET THEN
INSERT (ADDRESS, ALLIDS, BODY) VALUES (
SRC.ADDRESS,
CONVERT(VARCHAR,ROWID),
CONVERT(VARCHAR(MAX),
'<i>'+CONVERT(VARCHAR,SRC.DATED,101)+
' '+CONVERT(VARCHAR,SRC.DATED,8)+
':</i> <b>'+SRC.SUBJECT+'</b>'+CHAR(13)+
SRC.BODY+' (Message ID '+CONVERT(VARCHAR,SRC.ROWID)+')'
+CHAR(13)+CHAR(13)
)
);
END EDIT
下面是我的存储过程的代码...
ALTER PROCEDURE [system].[SendAccumulatedEmails]
AS
BEGIN
SET NOCOUNT ON;
DECLARE @SENTS BIGINT = 0;
DECLARE @ROWS TABLE (
ROWID ROWID,
DATED DATETIME,
ADDRESS NAME,
SUBJECT VARCHAR(1000),
BODY VARCHAR(MAX)
)
INSERT INTO @ROWS SELECT ROWID, DATED, ADDRESS, SUBJECT, BODY
FROM system.EMAILQUEUE
WHERE ACCUMULATE = 1 AND SENT IS NULL
ORDER BY ADDRESS, DATED
DECLARE @EMAILS TABLE (
ADDRESS NAME,
ALLIDS VARCHAR(1000),
BODY VARCHAR(MAX)
)
DECLARE @PRVRID ROWID = NULL, @CURRID ROWID = NULL
SELECT @CURRID = MIN(ROWID) FROM @ROWS
WHILE @CURRID IS NOT NULL BEGIN
MERGE @EMAILS AS DST
USING (SELECT * FROM @ROWS WHERE ROWID = @CURRID) AS SRC
ON SRC.ADDRESS = DST.ADDRESS
WHEN MATCHED THEN
UPDATE SET
DST.ALLIDS = DST.ALLIDS + ', ' + CONVERT(VARCHAR,ROWID),
DST.BODY = DST.BODY + '<i>'+CONVERT(VARCHAR,SRC.DATED,101)+' '
+CONVERT(VARCHAR,SRC.DATED,8)
+':</i> <b>'+SRC.SUBJECT+'</b>'+CHAR(13)+SRC.BODY
+' (Message ID '+CONVERT(VARCHAR,SRC.ROWID)+')'
+CHAR(13)+CHAR(13)
WHEN NOT MATCHED BY TARGET THEN
INSERT (ADDRESS, ALLIDS, BODY) VALUES (
SRC.ADDRESS,
CONVERT(VARCHAR,ROWID),
'<i>'+CONVERT(VARCHAR,SRC.DATED,101)+' '
+CONVERT(VARCHAR,SRC.DATED,8)+':</i> <b>'
+SRC.SUBJECT+'</b>'+CHAR(13)+SRC.BODY
+' (Message ID '+CONVERT(VARCHAR,SRC.ROWID)+')'
+CHAR(13)+CHAR(13));
SELECT @PRVRID = @CURRID, @CURRID = NULL
SELECT @CURRID = MIN(ROWID) FROM @ROWS WHERE ROWID > @PRVRID
END
DECLARE @MAILFROM VARCHAR(100) = system.getOption('MAILFROM'),
DECLARE @SMTPHST VARCHAR(100) = system.getOption('SMTPSERVER'),
DECLARE @SMTPUSR VARCHAR(100) = system.getOption('SMTPUSER'),
DECLARE @SMTPPWD VARCHAR(100) = system.getOption('SMTPPASS')
DECLARE @ADDRESS NAME, @BODY VARCHAR(MAX), @ADDL VARCHAR(MAX)
DECLARE @SUBJECT VARCHAR(1000) = 'Accumulated Emails from LIJSL'
DECLARE @PRVID NAME = NULL, @CURID NAME = NULL
SELECT @CURID = MIN(ADDRESS) FROM @EMAILS
WHILE @CURID IS NOT NULL BEGIN
SELECT @ADDRESS = ADDRESS, @BODY = BODY
FROM @EMAILS WHERE ADDRESS = @CURID
SELECT @BODY = @BODY + 'This is an automated message sent from an unmonitored mailbox.'+CHAR(13)+'Do not reply to this message; your message will not be read.'
SELECT @BODY =
'<style type="text/css">
* {font-family: Tahoma, Arial, Verdana;}
p {margin-top: 10px; padding-top: 10px; border-top: single 1px dimgray;}
p:first-child {margin-top: 10px; padding-top: 0px; border-top: none 0px transparent;}
</style>'
+ @BODY
exec system.LogIt @SUBJECT, @BODY
BEGIN TRY
exec system.SendMail @SMTPHST, @SMTPUSR, @SMTPPWD, @MAILFROM,
@ADDRESS, NULL, NULL, @SUBJECT, @BODY, 1
END TRY
BEGIN CATCH
DECLARE @EMSG NVARCHAR(2048) = 'system.EMAILQUEUE.AI:'+ERROR_MESSAGE()
SELECT @ADDL = 'TO:'+@ADDRESS+CHAR(13)+'SUBJECT:'+@SUBJECT+CHAR(13)+'BODY:'+@BODY
exec system.LogIt @EMSG,@ADDL
END CATCH
SELECT @PRVID = @CURID, @CURID = NULL
SELECT @CURID = MIN(ADDRESS) FROM @EMAILS WHERE ADDRESS > @PRVID
END
UPDATE system.EMAILQUEUE SET SENT = getdate()
FROM system.EMAILQUEUE E, @ROWS R WHERE E.ROWID = R.ROWID
END
I have a table in one of my databases which is a queue of emails. Emails to certain addresses get accumulated into one email, which is done by a sproc. In the sproc, I have a table variable which I use to build the accumulated bodies of the emails, and then loop through to send each email. In my table var I have my body column defined as VARCHAR(MAX)
, seeing as there could be any number of emails currently accumulated for a given email address. It seems though that even though my column is defined as VARCHAR(MAX)
it is behaving as if it were VARCHAR(4000)
and is truncating the data going into it, although it does NOT throw any exceptions, it just silently stops concatenating any more data after 4000 characters.
The MERGE statement is where it is building the accumulated email body into @EMAILS.BODY, which is the field that is truncating itself at 4000 characters.
EDIT
I have updated my MERGE statement in an attempt to cast the whole assigned string to VARCHAR(MAX), but it is still silently truncating itself to 4000 chars... here is my new MERGE:
MERGE @EMAILS AS DST
USING (SELECT * FROM @ROWS WHERE ROWID = @CURRID) AS SRC
ON SRC.ADDRESS = DST.ADDRESS
WHEN MATCHED THEN
UPDATE SET
DST.ALLIDS = DST.ALLIDS + ', ' + CONVERT(VARCHAR,ROWID),
DST.BODY = DST.BODY +
CONVERT(VARCHAR(MAX),
'<i>'+CONVERT(VARCHAR,SRC.DATED,101)+
' '+CONVERT(VARCHAR,SRC.DATED,8)+
':</i> <b>'+SRC.SUBJECT+'</b>'+CHAR(13)+
SRC.BODY+' (Message ID '+
CONVERT(VARCHAR,SRC.ROWID)+')'+
CHAR(13)+CHAR(13)
)
WHEN NOT MATCHED BY TARGET THEN
INSERT (ADDRESS, ALLIDS, BODY) VALUES (
SRC.ADDRESS,
CONVERT(VARCHAR,ROWID),
CONVERT(VARCHAR(MAX),
'<i>'+CONVERT(VARCHAR,SRC.DATED,101)+
' '+CONVERT(VARCHAR,SRC.DATED,8)+
':</i> <b>'+SRC.SUBJECT+'</b>'+CHAR(13)+
SRC.BODY+' (Message ID '+CONVERT(VARCHAR,SRC.ROWID)+')'
+CHAR(13)+CHAR(13)
)
);
END EDIT
Below is the code of my sproc...
ALTER PROCEDURE [system].[SendAccumulatedEmails]
AS
BEGIN
SET NOCOUNT ON;
DECLARE @SENTS BIGINT = 0;
DECLARE @ROWS TABLE (
ROWID ROWID,
DATED DATETIME,
ADDRESS NAME,
SUBJECT VARCHAR(1000),
BODY VARCHAR(MAX)
)
INSERT INTO @ROWS SELECT ROWID, DATED, ADDRESS, SUBJECT, BODY
FROM system.EMAILQUEUE
WHERE ACCUMULATE = 1 AND SENT IS NULL
ORDER BY ADDRESS, DATED
DECLARE @EMAILS TABLE (
ADDRESS NAME,
ALLIDS VARCHAR(1000),
BODY VARCHAR(MAX)
)
DECLARE @PRVRID ROWID = NULL, @CURRID ROWID = NULL
SELECT @CURRID = MIN(ROWID) FROM @ROWS
WHILE @CURRID IS NOT NULL BEGIN
MERGE @EMAILS AS DST
USING (SELECT * FROM @ROWS WHERE ROWID = @CURRID) AS SRC
ON SRC.ADDRESS = DST.ADDRESS
WHEN MATCHED THEN
UPDATE SET
DST.ALLIDS = DST.ALLIDS + ', ' + CONVERT(VARCHAR,ROWID),
DST.BODY = DST.BODY + '<i>'+CONVERT(VARCHAR,SRC.DATED,101)+' '
+CONVERT(VARCHAR,SRC.DATED,8)
+':</i> <b>'+SRC.SUBJECT+'</b>'+CHAR(13)+SRC.BODY
+' (Message ID '+CONVERT(VARCHAR,SRC.ROWID)+')'
+CHAR(13)+CHAR(13)
WHEN NOT MATCHED BY TARGET THEN
INSERT (ADDRESS, ALLIDS, BODY) VALUES (
SRC.ADDRESS,
CONVERT(VARCHAR,ROWID),
'<i>'+CONVERT(VARCHAR,SRC.DATED,101)+' '
+CONVERT(VARCHAR,SRC.DATED,8)+':</i> <b>'
+SRC.SUBJECT+'</b>'+CHAR(13)+SRC.BODY
+' (Message ID '+CONVERT(VARCHAR,SRC.ROWID)+')'
+CHAR(13)+CHAR(13));
SELECT @PRVRID = @CURRID, @CURRID = NULL
SELECT @CURRID = MIN(ROWID) FROM @ROWS WHERE ROWID > @PRVRID
END
DECLARE @MAILFROM VARCHAR(100) = system.getOption('MAILFROM'),
DECLARE @SMTPHST VARCHAR(100) = system.getOption('SMTPSERVER'),
DECLARE @SMTPUSR VARCHAR(100) = system.getOption('SMTPUSER'),
DECLARE @SMTPPWD VARCHAR(100) = system.getOption('SMTPPASS')
DECLARE @ADDRESS NAME, @BODY VARCHAR(MAX), @ADDL VARCHAR(MAX)
DECLARE @SUBJECT VARCHAR(1000) = 'Accumulated Emails from LIJSL'
DECLARE @PRVID NAME = NULL, @CURID NAME = NULL
SELECT @CURID = MIN(ADDRESS) FROM @EMAILS
WHILE @CURID IS NOT NULL BEGIN
SELECT @ADDRESS = ADDRESS, @BODY = BODY
FROM @EMAILS WHERE ADDRESS = @CURID
SELECT @BODY = @BODY + 'This is an automated message sent from an unmonitored mailbox.'+CHAR(13)+'Do not reply to this message; your message will not be read.'
SELECT @BODY =
'<style type="text/css">
* {font-family: Tahoma, Arial, Verdana;}
p {margin-top: 10px; padding-top: 10px; border-top: single 1px dimgray;}
p:first-child {margin-top: 10px; padding-top: 0px; border-top: none 0px transparent;}
</style>'
+ @BODY
exec system.LogIt @SUBJECT, @BODY
BEGIN TRY
exec system.SendMail @SMTPHST, @SMTPUSR, @SMTPPWD, @MAILFROM,
@ADDRESS, NULL, NULL, @SUBJECT, @BODY, 1
END TRY
BEGIN CATCH
DECLARE @EMSG NVARCHAR(2048) = 'system.EMAILQUEUE.AI:'+ERROR_MESSAGE()
SELECT @ADDL = 'TO:'+@ADDRESS+CHAR(13)+'SUBJECT:'+@SUBJECT+CHAR(13)+'BODY:'+@BODY
exec system.LogIt @EMSG,@ADDL
END CATCH
SELECT @PRVID = @CURID, @CURID = NULL
SELECT @CURID = MIN(ADDRESS) FROM @EMAILS WHERE ADDRESS > @PRVID
END
UPDATE system.EMAILQUEUE SET SENT = getdate()
FROM system.EMAILQUEUE E, @ROWS R WHERE E.ROWID = R.ROWID
END
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
更正...
表格可能由 varchar(max) 但您分配的值仅为 nvarchar(4000)
也就是说,
右侧将保持在 nvarchar(4000) 最大值 因为数据类型优先级。 nvarchar > varchar。当分配给 max 列时,它会截断
您必须确保 varchar 右侧的所有值
它仍然像整数除法...令我困惑的是 varchar 为 8000 时的 4000 限制...这意味着 nvarchar 某处。
对于 Nvarchar(Max) 我只是在 TSQL 中获取 4000 个字符?
Corrected...
The table may by varchar(max) but the values you assign are only nvarchar(4000)
That is,
The right hand side will stay at nvarchar(4000) maximum because of datatype precedence. nvarchar > varchar. When assigned to the max column it truncates
You'll have to ensure all values on the right at varchar
It's still like integer division... what confused me was the 4000 limit when varchar is 8000... this implies nvarchar somewhere.
For Nvarchar(Max) I am only getting 4000 characters in TSQL?
http://blogs.infosupport.com/blogs/marks/archive/2011/03/22/take-your-varchar-to-the-max.aspx?CommentPosted=true#commentmessage
这个问题上面的文章很好地解释了它的解决方案,解决方案是在串联中添加 VARCHAR(MAX)
AS IN
DECLARE @SQL VARCHAR(MAX)
设置@SQL=''
SET @SQL = @SQL + 'xxxxxx(n)'
http://blogs.infosupport.com/blogs/marks/archive/2011/03/22/take-your-varchar-to-the-max.aspx?CommentPosted=true#commentmessage
This problem and solution to it are very well explained in the above article, the solution is to add to the concatenation a VARCHAR(MAX)
AS IN
DECLARE @SQL VARCHAR(MAX)
SET @SQL = ''
SET @SQL = @SQL + 'xxxxxx(n)'
我怀疑问题出在字符串和转换操作上。尝试将转换更改为 VARCHAR(max) 或将整个表达式转换为 VARCHAR(max)。
I suspect the problem lies in the string and conversion operations. Try changing your conversions to VARCHAR(max) or converting the entire expression to VARCHAR(max).
gbn 和 Jeffrey,谢谢你们的帮助,你们让我朝着正确的方向前进。尽管经过一些记录和检查,它实际上可以很好地连接我的字符串。
问题不在于我的列数据类型或长度,而在于对我的 .NET
SendMail
过程的调用,该过程仅接受 NVARCHAR(4000) 作为 BODY 参数.... NETSqlString
类型。因此,现在我正在寻找如何将更长的字符串传递到 CLR 汇编函数中。
gbn and Jeffrey, thank you for you help, you got me going in the right direction. Though after some logging and checking, it actually is concatenating my string just fine.
The problem was not with my column datatype or length, but with the call to my .NET
SendMail
procedure, which is only accepting NVARCHAR(4000) for the BODY argument... the apparent translation of the .NETSqlString
type.So now I am off on a hunt to figure how to pass longer strings into a CLR assembly function.