【问题标题】:Microsoft SQL Server 2008 cursorMicrosoft SQL Server 2008 游标
【发布时间】:2014-05-30 05:58:24
【问题描述】:

我有一个#temptable,其中包含正在使用游标读取的地址信息。光标效果很好。游标中的 if 语句对 #table 中的第一条记录 100% 有效,但对于第二条记录,它似乎总是返回 false。

if 语句的想法是检查 db 中地址表中存在的值,然后如果它确实获取 id。如果没有,则插入该行并获取 ID。独立 if 语句 100% 有效。我将它添加到光标中的那一刻,它就中断了。就好像变量被某种方式覆盖了一样。

因此,如果我的#table 中包含同一行,则该行仍会两次插入到 address_table 中,而不仅仅是一次。第一次插入地址不存在,第二次返回表中地址的id

我已经取出了我执行 convert() 的打印语句,但它似乎没有什么不同。

这是独立的 if 语句。

DECLARE @COMP_NUM AS VARCHAR(100) = '4'
DECLARE @COMP_NAME AS VARCHAR(100) = 'TAGASTE'
DECLARE @STREET_NUM AS VARCHAR(100) = '150'
DECLARE @STREET_NAME AS VARCHAR(100) = 'WILLSON'
DECLARE @STREET_TYPE AS INT = 1
DECLARE @SUB AS VARCHAR(100) = 'FAIRLANDS'
DECLARE @CITY AS VARCHAR(100) = 'JOHANNESBURG'
DECLARE @HOMEPHONE AS VARCHAR(100) = '0112355566'
DECLARE @EXISTS AS INT
DECLARE @ADD_ID AS INT

SET @EXISTS = (SELECT COUNT(ID) FROM CARETEAMZ..ADDRESS_BOOK WHERE HOME_PHONE=@HOMEPHONE 
AND COMPLEX_NUMBER = @COMP_NUM
AND COMPLEX_NAME = @COMP_NAME
AND STREET_NUMBER = @STREET_NUM
AND STREET_NAME = @STREET_NAME
AND STREET_TYPE = @STREET_TYPE
AND SUBURB = @SUB
AND CITY = @CITY)
PRINT ('COUNT ROWS: ' + CONVERT(CHAR(6),@EXISTS))

IF (CONVERT(INT,@EXISTS) > 0)               
BEGIN
    PRINT ('RECORD EXISTS')
    SET @ADD_ID = (SELECT ID FROM CARETEAMZ..ADDRESS_BOOK WHERE HOME_PHONE = @HOMEPHONE 
                    AND COMPLEX_NUMBER = @COMP_NUM
                    AND COMPLEX_NAME = @COMP_NAME
                    AND STREET_NUMBER = @STREET_NUM
                    AND STREET_NAME = @STREET_NAME
                    AND STREET_TYPE = @STREET_TYPE
                    AND SUBURB = @SUB
                    AND CITY = @CITY) 
    PRINT ('ADDRESS ID: ' + CONVERT(CHAR(6),@ADD_ID))
END
ELSE
BEGIN
    PRINT ('RECORD DOES NOT EXIST')
    INSERT INTO CARETEAMZ..ADDRESS_BOOK (HOME_PHONE, COMPLEX_NUMBER, COMPLEX_NAME, STREET_NUMBER, STREET_NAME,STREET_TYPE, SUBURB, CITY)
    VALUES (@HOMEPHONE, @COMP_NUM,@COMP_NAME, @STREET_NUM,@STREET_NAME,@STREET_TYPE,@SUB,@CITY)

    SET @ADD_ID = (SELECT ID FROM CARETEAMZ..ADDRESS_BOOK WHERE HOME_PHONE = @HOMEPHONE 
                    AND COMPLEX_NUMBER = @COMP_NUM
                    AND COMPLEX_NAME = @COMP_NAME
                    AND STREET_NUMBER = @STREET_NUM
                    AND STREET_NAME = @STREET_NAME
                    AND STREET_TYPE = @STREET_TYPE
                    AND SUBURB = @SUB
                    AND CITY = @CITY) 
    PRINT ('ADDRESS ID: ' + CONVERT(char(6),@ADD_ID))
END

这是我的完整光标

DECLARE @COMP AS VARCHAR(500)
DECLARE @COMP_LENGTH AS INT
DECLARE @COMP_INDEX AS INT
DECLARE @COMP_NUM AS VARCHAR(50)
DECLARE @COMP_NAME AS VARCHAR(100)
DECLARE @STREET AS VARCHAR(500)
DECLARE @STREET_LENGTH AS INT
DECLARE @STREET_INDEX AS INT
DECLARE @STREET_NUM AS VARCHAR(50)
DECLARE @STREET_NAME AS VARCHAR(100)
DECLARE @STREET_VALUE AS VARCHAR(200)
DECLARE @STREET_TYPE AS INT
DECLARE @SUB AS VARCHAR(100)
DECLARE @CITY AS VARCHAR(100)
DECLARE @HOMEPHONE AS VARCHAR(100)
SET @CITY = 'JOHANNESBURG'

DECLARE ADD_CURSOR CURSOR FOR SELECT [HOME],[COMPLEX], [STREET] FROM #TEMPADD
OPEN ADD_CURSOR
FETCH NEXT FROM ADD_CURSOR INTO @HOMEPHONE,@COMP,@STREET;
    WHILE @@FETCH_STATUS = 0   
        BEGIN
            --COMPLEX DETAILS
            PRINT('-- COMPLEX DETAILS --')
            PRINT(@COMP) 
            SET @COMP_INDEX = CHARINDEX(',',@COMP)
            PRINT(@COMP_INDEX)
            IF (@COMP_INDEX > 0)
            BEGIN
                    PRINT('COMPLEX TRUE')
                    SET @COMP_NUM = LTRIM(RTRIM((SUBSTRING(@COMP,0,@COMP_INDEX))))
                    SET @COMP_NAME = LTRIM(RTRIM((SUBSTRING(@COMP,@COMP_INDEX+1,LEN(@COMP)))))                  
            END
            ELSE 
            BEGIN 
                    PRINT('COMPLEX FALSE')
                    SET @COMP_NUM = NULL
                    SET @COMP_NAME = NULL
            END
            PRINT('COMPLEX NUMBER: ' + CONVERT(VARCHAR(50),@COMP_NUM))
            PRINT('COMPLEX NAME: ' + @COMP_NAME)

            -- STREET ADDRESS
            PRINT('-- STREET NAME --')
            PRINT(@STREET) 
            SET @STREET_INDEX = CHARINDEX(',',@STREET)
            PRINT(@STREET_INDEX)
            IF (@STREET_INDEX > 0)
            BEGIN
                    PRINT('STREET TRUE')
                    SET @STREET_NUM = LTRIM(RTRIM((SUBSTRING(@STREET,0,@STREET_INDEX))))
                    SET @STREET = (SUBSTRING(@STREET,@STREET_INDEX+1,LEN(@STREET)))
                    PRINT(@STREET)
                    SET @STREET_INDEX = CHARINDEX(',',@STREET)
                    SET @STREET_NAME = LTRIM(RTRIM((SUBSTRING(@STREET,0,CHARINDEX(' ',@STREET)))))
                    SET @SUB = LTRIM(RTRIM((SUBSTRING(@STREET,@STREET_INDEX+1,LEN(@STREET)))))
                    SET @STREET_VALUE = SUBSTRING(@STREET,0,CHARINDEX(',',@STREET))
                    SET @STREET_TYPE = (SELECT ID FROM CARETEAMZ..STREET_TYPE WHERE STREET_TYPE = RTRIM(LTRIM((SUBSTRING(@STREET_VALUE,LEN(@STREET_VALUE)-CHARINDEX(' ',REVERSE(@STREET_VALUE))+2,LEN(@STREET_VALUE)-CHARINDEX(',',@STREET_VALUE))))))                  
            END
            ELSE 
            BEGIN 
                    PRINT('STREET FALSE')
                    SET @STREET_NUM = NULL
                    SET @STREET_NAME = NULL
                    SET @STREET_TYPE = NULL
                    SET @SUB = NULL
            END
            PRINT('STREET NUMBER: ' + @STREET_NUM)
            PRINT('STREET NAME: ' + @STREET_NAME)
            PRINT('STREET TYPE: ' + CONVERT(VARCHAR(10),@STREET_TYPE))
            PRINT('SUBURB: ' + @SUB)

            --CHECK IF THE ADDRESS / PHONE NUMBER ALREADY EXISTS IN THE DB
            DECLARE @ADD_ID AS INT
            DECLARE @EXISTS AS INT

            SET @EXISTS = (SELECT COUNT(ID) FROM CARETEAMZ..ADDRESS_BOOK WHERE HOME_PHONE = @HOMEPHONE 
            AND COMPLEX_NUMBER = @COMP_NUM
            AND COMPLEX_NAME = @COMP_NAME
            AND STREET_NUMBER = @STREET_NUM
            AND STREET_NAME = @STREET_NAME
            AND STREET_TYPE = @STREET_TYPE
            AND SUBURB = @SUB
            AND CITY = @CITY)
            PRINT ('COUNT ROWS: ' + CONVERT(CHAR(6),@EXISTS))
            IF (@EXISTS > 0)                
            BEGIN
                PRINT ('RECORD EXISTS')
                SET @ADD_ID = (SELECT ID FROM CARETEAMZ..ADDRESS_BOOK WHERE HOME_PHONE = @HOMEPHONE 
                                AND COMPLEX_NUMBER = @COMP_NUM
                                AND COMPLEX_NAME = @COMP_NAME
                                AND STREET_NUMBER = @STREET_NUM
                                AND STREET_NAME = @STREET_NAME
                                AND STREET_TYPE = @STREET_TYPE
                                AND SUBURB = @SUB
                                AND CITY = @CITY)

                PRINT ('ADDRESS ID: ' + CONVERT(CHAR(6),@ADD_ID))
            END
            ELSE
            BEGIN
                PRINT ('RECORD DOES NOT EXIST')
                INSERT INTO CARETEAMZ..ADDRESS_BOOK (HOME_PHONE, COMPLEX_NUMBER, COMPLEX_NAME, STREET_NUMBER, STREET_NAME,STREET_TYPE, SUBURB, CITY)
                VALUES (@HOMEPHONE, @COMP_NUM,@COMP_NAME, @STREET_NUM,@STREET_NAME,@STREET_TYPE,@SUB,@CITY)

                SET @ADD_ID = (SELECT ID FROM CARETEAMZ..ADDRESS_BOOK WHERE HOME_PHONE = @HOMEPHONE 
                                AND COMPLEX_NUMBER = @COMP_NUM
                                AND COMPLEX_NAME = @COMP_NAME
                                AND STREET_NUMBER = @STREET_NUM
                                AND STREET_NAME = @STREET_NAME
                                AND STREET_TYPE = @STREET_TYPE
                                AND SUBURB = @SUB
                                AND CITY = @CITY) 

                PRINT ('ADDRESS ID: ' + CONVERT(char(6),@ADD_ID))

            END

            --FETCH NEXT
            FETCH NEXT FROM ADD_CURSOR INTO @HOMEPHONE,@COMP, @STREET;
        END
CLOSE ADD_CURSOR;
DEALLOCATE ADD_CURSOR;

好的,我已经查看了@HLGEM 和@kuru kuru pa 的答案。 @HLGEM 这只是更大游标的一小部分,不幸的是 MERGER SQL 无法满足我当前的需求。我不担心性能,因为这只是将数据导入我的数据库。

@kuru kuru pa 的回答很好,但不幸的是,我似乎仍然再次遇到同样的问题。我第一次运行游标时它运行 100%。插入所有值,第二次它应该只返回 ID,但 id 不返回任何内容并插入重复项。

下面是 2 个带有一些虚拟数据的表创建语句。

临时表

CREATE TABLE #TEMPADD
(
    HOME VARCHAR(100) NULL,
    COMPLEX VARCHAR(100) NULL,
    STREET VARCHAR(100) NULL,
)

INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES ('011 679 6787',  '32,Tagaste',   '150,Willson Street, Land')
INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES ('011 679 1909',  NULL,   '29,Bunkara Street, Rio')
INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES ('011 689 2630',  NULL,   '275,Kings Lynne Road, Glen')
INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES (NULL,    NULL,   '275,Kings Lynne Road, Glen')
INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES ('011 799 5917',  '5,The Vineyard',   '45,Hilary Road, Ridge')
INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES (NULL,    '5,The Vineyard',   '45,Hilary Road, Ridge')
INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES ('011 679 5857',  NULL,   '11A,Alexandra Street, Florida')
INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES ('011 679 5857',  NULL,   '11A,Alexandra Street, Florida')
INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES ('011 679 3225',  NULL,   '752, Without Avenue, Weltevreden')
INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES ('011 679 8909',  NULL,   '18,Smit Street,Land')
INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES ('011 679 8909',  '512,Athenian View',    '158,Smit Street, Fairland')
INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES (NULL,    '741,Eagle Trace Landing',  'Eagle Canyon Golf Estate, Honey')
INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES (NULL,    '741,Eagle Trace Landing',  'Eagle Canyon Golf Estate, Honey')
INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES (NULL,    '741,Eagle Trace Landing',  'Eagle Canyon Golf Estate, Honey')
INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES (NULL,    '741,Eagle Trace Landing',  'Eagle Canyon Golf Estate, Honey')
INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES (NULL,    '741,Eagle Trace Landing',  'Eagle Canyon Golf Estate, Honey')
INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES (NULL,    '741,Eagle Trace Landing',  'Eagle Canyon Golf Estate, Honey')
INSERT INTO #TEMPADD (HOME,COMPLEX,STREET) VALUES (NULL,    NULL,   '106A,3rd Avenue, Land')

地址表

CREATE TABLE [dbo].[ADDRESS_BOOK](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [PoBox] [varchar](50) NULL,
    [Complex_Number] [varchar](50) NULL,
    [Complex_Name] [varchar](100) NULL,
    [Street_Number] [varchar](50) NULL,
    [Street_Name] [varchar](100) NULL,
    [Street_Type] [int] NULL,
    [Suburb] [varchar](100) NULL,
    [City] [varchar](50) NULL,
    [Code] [varchar](50) NULL,
    [Home_Phone] [varchar](50) NULL
) ON [PRIMARY]

【问题讨论】:

  • 老实说,如果您包含一些 create table 脚本,我会更想看看这个,这样我就可以运行它了。我不明白您为什么将@Exists 转换为int,而它是int
  • 我已经发布了您创建表格的代码...感谢您到目前为止的帮助...

标签: sql-server sql-server-2008 variables if-statement database-cursor


【解决方案1】:

您到底为什么要使用光标来执行此操作?建议您阅读这篇文章,看看使用基于集合的技术有多容易(以及更快):

http://wiki.lessthandot.com/index.php/Cursors_and_How_to_Avoid_Them

【讨论】:

  • 顺便说一句很棒的网站..不幸的是合并sql不会解决我的问题...这个光标只是大局的一小部分...不要担心回合的性能它只会运行一次以填充空白数据库...我已经发布了光标运行的表的代码...
【解决方案2】:

有很多代码要通读...

第一个观察结果(不是您问题的答案,而是 FWIW)是您可能使用了错误的工具来完成这项工作。不确定这里是否需要游标。总之……

现在只是抽查...(除非您想添加@martin 建议的创建表脚本...(如果您这样做,我会给您一个更全面的答案)

您对@Exists 的使用是多余的。基本上,您使用@Exists 来告诉您@Add_ID 是否存在。这是一种间接/冗余的方法。

If @Exists exists, then @Add_ID exists.

您可以使用更无聊的重言式(直接方法):

If @Add_ID exists, then @Add_ID exists.

所以你的代码可能是这样的:

SET @Exists = ...
If @Exits > 0...

SET @ADD_ID = (SELECT ID FROM CARETEAMZ..ADDRESS_BOOK WHERE HOME_PHONE = @HOMEPHONE AND ...)
IF @ADD_ID IS NOT NULL
BEGIN
   PRINT(...)
END
ELSE...

【讨论】: