{"id":4015,"date":"2010-01-03T11:13:37","date_gmt":"2010-01-03T10:13:37","guid":{"rendered":"http:\/\/www.glorf.it\/blog\/?p=4015"},"modified":"2010-01-05T13:34:27","modified_gmt":"2010-01-05T12:34:27","slug":"vorsicht-bei-cursor-auf-tvfview-mit-pivot","status":"publish","type":"post","link":"http:\/\/www.glorf.it\/blog\/2010\/01\/03\/sql-talk\/vorsicht-bei-cursor-auf-tvfview-mit-pivot","title":{"rendered":"Vorsicht bei Cursor auf TVF\/View mit PIVOT"},"content":{"rendered":"<p>K&#252;rzlich wurde der kummulative Update 7 (CU7) f&#252;r den SQL Server 2005 SP3 ver&#246;ffentlicht. Darin wird ein Problem beseitig, das man nur schwer entdecken kann, wenn man betroffen ist: Bei einem Cursor auf eine View oder eine Table-Valued-Fumction kann der Cursor falsche Ergebnisse liefern. Hier ein Repro, dass auch mit SQL Server 2008 funktioniert:<\/p>\n<p><code lang=\"sql\">SET NOCOUNT ON;<br \/>\ngo<br \/>\nUSE tempdb<br \/>\ngo<br \/>\nBEGIN TRY DROP TABLE dbo.tg_table1 END TRY BEGIN CATCH END CATCH;<br \/>\nBEGIN TRY DROP TABLE dbo.tg_table2 END TRY BEGIN CATCH END CATCH;<br \/>\nBEGIN TRY DROP FUNCTION dbo.f_PivotAndOuterJoin END TRY BEGIN CATCH END CATCH;<br \/>\ngo<br \/>\n&#8211; create table 1<br \/>\nSELECT cy, value, type<br \/>\n\tINTO dbo.tg_table1<br \/>\n\tFROM (\tSELECT 2002, 1, 3 UNION ALL<br \/>\n\t\t\tSELECT 2003, 1, 3 UNION ALL<br \/>\n\t\t\tSELECT 2004, 1, 3 UNION ALL<br \/>\n\t\t\tSELECT 2005, 1, 3 UNION ALL<br \/>\n\t\t\tSELECT 2006, 1, 3 UNION ALL<br \/>\n\t\t\tSELECT 2007, 1, 3 UNION ALL<br \/>\n\t\t\tSELECT 2008, 1, 3 UNION ALL<br \/>\n\t\t\tSELECT 2009, 1, 3 UNION ALL<br \/>\n\t\t\tSELECT 2008, 1, 3 UNION ALL<br \/>\n\t\t\tSELECT 2009, 1, 3) AS x(cy, value, type);<br \/>\ngo<br \/>\n&#8211; create table 2<br \/>\nSELECT cy<br \/>\n\tINTO dbo.tg_table2<br \/>\n\tFROM (\tSELECT 2003 UNION ALL<br \/>\n\t\t\tSELECT 2004 UNION ALL<br \/>\n\t\t\tSELECT 2005 UNION ALL<br \/>\n\t\t\tSELECT 2006 UNION ALL<br \/>\n\t\t\tSELECT 2007 UNION ALL<br \/>\n\t\t\tSELECT 2008) AS x(cy);<\/p>\n<p>CREATE CLUSTERED INDEX c_1 ON dbo.tg_table2 (cy);<br \/>\ngo<br \/>\nCREATE FUNCTION dbo.f_PivotAndOuterJoin ()<br \/>\nRETURNS TABLE AS<br \/>\nRETURN<br \/>\n\tSELECT d.cy, [3] AS value<br \/>\n\t\tFROM tg_table1 AS t1<br \/>\n\t\t\tPIVOT ( MAX(value) FOR t1.type in ([3]) ) AS d<br \/>\n\t\tLEFT OUTER JOIN dbo.tg_table2 AS t2<br \/>\n\t\t\tON\td.cy=t2.cy<\/p>\n<p>go<\/p>\n<p>DECLARE @year int;<\/p>\n<p>PRINT &#x0027;wrong results:&#x0027;;<\/p>\n<p>DECLARE cCursor CURSOR FOR<br \/>\n\tSELECT cy FROM dbo.f_PivotAndOuterJoin() ORDER BY cy;<\/p>\n<p>OPEN cCursor;<br \/>\nFETCH NEXT FROM cCursor INTO @year;<\/p>\n<p>WHILE @@FETCH_STATUS = 0 BEGIN<br \/>\n\tPRINT convert(varchar, @year);<\/p>\n<p>\tFETCH NEXT FROM cCursor INTO @year;<br \/>\nEND<br \/>\nCLOSE cCursor;<br \/>\nDEALLOCATE cCursor;<\/p>\n<p>PRINT &#x0027; &#x0027;; &#8211; only to get an empty line<br \/>\ngo<br \/>\nDECLARE @year int;<\/p>\n<p>PRINT &#x0027;correct result (with STATIC cursor):&#x0027;;<\/p>\n<p>&#8211; STATIC, FAST_FORWARD, or READ_ONLY are OK,<br \/>\n&#8211; but FORWARD_ONLY, DYNAMIC, SCROLL, or KEYSET are not OK<br \/>\nDECLARE cCursor CURSOR STATIC FOR<br \/>\n\tSELECT cy FROM dbo.f_PivotAndOuterJoin () ORDER BY cy;<\/p>\n<p>OPEN cCursor;<br \/>\nFETCH NEXT FROM cCursor INTO @year;<\/p>\n<p>WHILE @@FETCH_STATUS = 0 BEGIN<br \/>\n\tPRINT convert(varchar, @year);<\/p>\n<p>\tFETCH NEXT FROM cCursor INTO @year;<br \/>\nEND<br \/>\nCLOSE cCursor;<br \/>\nDEALLOCATE cCursor;<\/p>\n<p>\/* output:<\/p>\n<p>wrong results:<br \/>\n2009<br \/>\n2004<br \/>\n2005<br \/>\n2006<br \/>\n2007<br \/>\n2008<br \/>\n2009<\/p>\n<p>correct result (with STATIC cursor):<br \/>\n2002<br \/>\n2003<br \/>\n2004<br \/>\n2005<br \/>\n2006<br \/>\n2007<br \/>\n2008<br \/>\n2009<br \/>\n*\/<\/code><\/p>\n<p>Die genauen Bedingungen sind im <a href=\"http:\/\/support.microsoft.com\/kb\/976565\/en\">KB-Artikel 976565<\/a> beschrieben:<\/p>\n<blockquote><p># In Microsoft SQL Server 2005, you have a query that opens a cursor of type FORWARD_ONLY, DYNAMIC, SCROLL or KEYSET.<br \/>\n# The cursor is in an inline table-valued function or a view.<br \/>\n# The inline table-valued function or the view uses either a PIVOT operator or an UNPIVOT operator.<br \/>\n# In addition to the source table of the PIVOT or UNPIVOT operator, there is at least one other table that is referenced in the inline table-valued function or the view.<\/p><\/blockquote>\n<p>Alle Bedingungen m&#252;ssen zutreffen. Meine intuitive Reaktion ist berechenbar: schon wieder ein Argument gegen Cursor. Aber leider gibt es immer noch einige Situationen in denen man unbedingt Cursor ben&#246;tigt. Das oben gesagt trifft nat&#252;rlich auch f&#252;r client-seitige Cursor zu.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>K&#252;rzlich wurde der kummulative Update 7 (CU7) f&#252;r den SQL Server 2005 SP3 ver&#246;ffentlicht. Darin wird ein Problem beseitig, das man nur schwer entdecken kann, wenn man betroffen ist: Bei einem Cursor auf eine View oder eine Table-Valued-Fumction kann der Cursor falsche Ergebnisse liefern. Hier ein Repro, dass auch mit SQL Server 2008 funktioniert: SET [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[3],"tags":[48,303,51,305],"_links":{"self":[{"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/posts\/4015"}],"collection":[{"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/comments?post=4015"}],"version-history":[{"count":6,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/posts\/4015\/revisions"}],"predecessor-version":[{"id":4087,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/posts\/4015\/revisions\/4087"}],"wp:attachment":[{"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/media?parent=4015"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/categories?post=4015"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/tags?post=4015"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}