【问题标题】:Returning Rows Created in a Cursor From a Stored Procedure从存储过程返回游标中创建的行
【发布时间】:2017-09-26 18:36:16
【问题描述】:

我有一个包含三列的表格,每列包含逗号分隔的数据,例如

AAA,BBB,CCC,DDD,...

我想返回一个CROSS JOIN,其中包含所有列中所有标记的所有可能组合,以及每行中的一些其他列。

我有一个 Split 函数,它返回表中的标记。我传入每一列并返回一堆行。

我能想到的最好方法是使用游标,一次取每一行。完成 CROSS JOIN 后,我将所有计算的行写入工作/临时表。处理完所有行后,我从工作/临时表中选择以返回计算的行。

我的问题:有没有办法在没有工作/临时表的情况下做到这一点?

我现在的代码是:

DECLARE cPKG CURSOR FAST_FORWARD FOR SELECT ID, SEARCH, COUNTY, COMPANY FROM DEV..EXPPKG WITH(NOLOCK)
OPEN cPKG
FETCH NEXT FROM cPKG INTO @ID, @SEARCH, @COUNTY, @COMPANY
WHILE @@FETCH_STATUS = 0
BEGIN
    INSERT INTO DEV..PKG_DUMP_WORK
                (ID,
                    PKG_CODE,
                    PRICE,
                    CNTY,
                    CPNY,
                    SRCH,
                    SEARCH,
                    COUNTY,
                    COMPANY,
                    SRCH_COUNT,
                    UPDT_DT,
                    UPDT_BY,
                    UPDT_CMT)
    SELECT PKG.ID,
            PKG.PKG_CODE,
            PKG.PRICE,
            CNTY.VALUE AS CNTY,
            CPNY.VALUE AS CPNY,
            SRCH.VALUE AS SRCH,
            PKG.SEARCH,
            PKG.COUNTY,
            PKG.COMPANY,
            PKG.SRCH_COUNT,
            PKG.UPDT_DT,
            PKG.UPDT_BY,
            PKG.UPDT_CMT
    FROM   (SELECT *
            FROM   DEV..EXPPKG WITH(NOLOCK)
            WHERE  ID = @ID) PKG
            CROSS JOIN DBO.Split(@SEARCH, ',') SRCH -- AAA,BBB,CCC...
            CROSS JOIN DBO.Split(@COUNTY, ',') CNTY -- DDD,EEE,FFF..
            CROSS JOIN DBO.Split(@COMPANY, ',') CPNY -- GGG,HHH,KKK...

    FETCH NEXT FROM cPKG INTO @ID, @SEARCH, @COUNTY, @COMPANY
END
CLOSE cPKG
DEALLOCATE cPKG

一些数据:

    INSERT INTO [EXPPKG] ( PKG_CODE, PRICE, SEARCH, COUNTY, COMPANY, SRCH_COUNT, UPDT_DT, UPDT_BY, UPDT_CMT ) VALUES ( 'A-2', 999, 'CO,ER,FC,HB,ST,TX', 'BX,KG,QN,RI', ',AAN,ALR,CITI,GRANITE,HARB,LLS,LTTA,MADI,NARROW,REGENCY,', 6, NULL, NULL, NULL );
    INSERT INTO [EXPPKG] ( PKG_CODE, PRICE, SEARCH, COUNTY, COMPANY, SRCH_COUNT, UPDT_DT, UPDT_BY, UPDT_CMT ) VALUES ( 'AM-2', 999, 'CO,ER,FC,HB,ST,TX', 'MA', ',ALR,CITI,GRANITE,INTER,LTTA,MADI,SKYLINE,', 6, NULL, NULL, NULL );
    INSERT INTO [EXPPKG] ( PKG_CODE, PRICE, SEARCH, COUNTY, COMPANY, SRCH_COUNT, UPDT_DT, UPDT_BY, UPDT_CMT ) VALUES ( 'B-2', 999, 'AR,CO,ER,FC,HB,HI,HL,ST,TX', 'BX,KG,QN,RI', ',C&C,LTTA,', 9, NULL, NULL, NULL );
    INSERT INTO [EXPPKG] ( PKG_CODE, PRICE, SEARCH, COUNTY, COMPANY, SRCH_COUNT, UPDT_DT, UPDT_BY, UPDT_CMT ) VALUES ( 'CA-2', 999, 'CO,ER,FC,HB,HI,ST,TX', 'BX,KG,MA,QN,RI', ',CANY,CHATHAM,TRAK,', 7, NULL, NULL, NULL );
    INSERT INTO [EXPPKG] ( PKG_CODE, PRICE, SEARCH, COUNTY, COMPANY, SRCH_COUNT, UPDT_DT, UPDT_BY, UPDT_CMT ) VALUES ( 'CT-4', 999, 'CO,ER,FC,HB', 'BX,KG,MA,QN,RI', ',CLTLTNY,CTALB,CTIM,CTIM-711,CTIM-CC,CTIM-Q,CTIM-R,CTIMR-O,FNT,FNT-A,FNT-AG,FNT-N,FNT-R,NYLS,TICOR,TICORROC,FNT-RAM,', 4, NULL, NULL, NULL );

【问题讨论】:

  • 你为什么要使用光标?这里根本不需要游标。这应该是一个插入语句。您的拆分功能有什么?更多循环?分隔数据是糟糕的设计,因为它违反了 1NF。而那些 NOLOCK 的暗示也比大多数人意识到的要险恶得多。
  • SL:数据不是我设计的,无法更改。这些列包含逗号分隔的数据,如 AAA、BBB、CCC.. 每行包含三个这样的列。列可能包含 1-N 个标记 - 无法预测。 SPLIT 获取一列并将其转换为每个分隔标记的一行,以便它们可以被交叉。如果你有办法一次性做到这一点,我想听听。
  • 我相信 CROSS APPLY 是答案,但数据库兼容性是古老的。切换到一个新的数据库,它的工作原理。

标签: sql-server tsql cursor temp-tables


【解决方案1】:

您可以将整个光标替换为基于集合的插入。我还要提醒你不要使用那个 NOLOCK 提示。它可以并且将返回丢失和/或重复的行。连同其他一些令人讨厌的事情。 http://blogs.sqlsentry.com/aaronbertrand/bad-habits-nolock-everywhere/

INSERT INTO DEV..PKG_DUMP_WORK
(
    ID,
    PKG_CODE,
    PRICE,
    CNTY,
    CPNY,
    SRCH,
    SEARCH,
    COUNTY,
    COMPANY,
    SRCH_COUNT,
    UPDT_DT,
    UPDT_BY,
    UPDT_CMT
)
SELECT PKG.ID,
    PKG.PKG_CODE,
    PKG.PRICE,
    CNTY.VALUE AS CNTY,
    CPNY.VALUE AS CPNY,
    SRCH.VALUE AS SRCH,
    PKG.SEARCH,
    PKG.COUNTY,
    PKG.COMPANY,
    PKG.SRCH_COUNT,
    PKG.UPDT_DT,
    PKG.UPDT_BY,
    PKG.UPDT_CMT
FROM   DEV..EXPPKG PKG WITH(NOLOCK)
CROSS APPLY DBO.Split(PKG.SEARCH, ',') SRCH -- AAA,BBB,CCC...
CROSS APPLY DBO.Split(PKG.COUNTY, ',') CNTY -- DDD,EEE,FFF..
CROSS APPLY DBO.Split(PKG.COMPANY, ',') CPNY -- GGG,HHH,KKK...

【讨论】:

  • CROSS APPLY 是秘密,我想。不幸的是,CROSS APPLY 似乎不喜欢将该合格列作为拆分的参数。它在那个时期呕吐。
  • “经期吐槽”是什么意思?我假设您收到错误消息?分享该错误消息将有助于我们在很大程度上理解问题。
  • Msg 102, Level 15, State 1, Line 101 '.' 附近的语法不正确。
  • 你使用的是什么版本的sql server?或者兼容模式设置为什么?我感觉你正在运行 sql 2000 或 2000 兼容模式。
  • 我想知道这是否是数据库版本问题:“SEARCH”不是公认的表提示选项。如果它打算作为表值函数或 CHANGETABLE 函数的参数,请确保您的数据库兼容模式设置为 90。
【解决方案2】:

尝试使用CROSS APPLY

SELECT *
FROM   DEV..EXPPKG WITH(NOLOCK)
CROSS APPLY DBO.Split(SEARCH, ',') SRCH -- AAA,BBB,CCC...
CROSS APPLY DBO.Split(COUNTY, ',') CNTY -- DDD,EEE,FFF..
CROSS APPLY DBO.Split(COMPANY, ',') CPNY

这是使用通用拆分函数的解决方案

DROP FUNCTION SplitString
GO
CREATE FUNCTION SplitString
(    
      @Input NVARCHAR(MAX),
      @Character CHAR(1)
)
RETURNS @Output TABLE (
      Value NVARCHAR(1000)
)
AS
BEGIN
      DECLARE @StartIndex INT, @EndIndex INT
      SET @StartIndex = 1
      IF SUBSTRING(@Input, LEN(@Input) - 1, LEN(@Input)) <> @Character
      BEGIN
            SET @Input = @Input + @Character
      END

      WHILE CHARINDEX(@Character, @Input) > 0
      BEGIN
            SET @EndIndex = CHARINDEX(@Character, @Input)

            INSERT INTO @Output(Value)
            SELECT SUBSTRING(@Input, @StartIndex, @EndIndex - 1)

            SET @Input = SUBSTRING(@Input, @EndIndex + 1, LEN(@Input))
      END

      RETURN
END
GO

    declare @EXPPKG table ( ID int identity,  PKG_CODE varchar(5) , PRICE int , SEARCH varchar(255), COUNTY varchar(255), COMPANY varchar(255), SRCH_COUNT varchar(255), UPDT_DT varchar(255), UPDT_BY varchar(255), UPDT_CMT varchar(255))
    INSERT INTO @EXPPKG  ( PKG_CODE, PRICE, SEARCH, COUNTY, COMPANY, SRCH_COUNT, UPDT_DT, UPDT_BY, UPDT_CMT ) VALUES ( 'A-2', 999, 'CO,ER,FC,HB,ST,TX', 'BX,KG,QN,RI', ',AAN,ALR,CITI,GRANITE,HARB,LLS,LTTA,MADI,NARROW,REGENCY,', 6, NULL, NULL, NULL );
    INSERT INTO @EXPPKG  ( PKG_CODE, PRICE, SEARCH, COUNTY, COMPANY, SRCH_COUNT, UPDT_DT, UPDT_BY, UPDT_CMT ) VALUES ( 'AM-2', 999, 'CO,ER,FC,HB,ST,TX', 'MA', ',ALR,CITI,GRANITE,INTER,LTTA,MADI,SKYLINE,', 6, NULL, NULL, NULL );
    INSERT INTO @EXPPKG  ( PKG_CODE, PRICE, SEARCH, COUNTY, COMPANY, SRCH_COUNT, UPDT_DT, UPDT_BY, UPDT_CMT ) VALUES ( 'B-2', 999, 'AR,CO,ER,FC,HB,HI,HL,ST,TX', 'BX,KG,QN,RI', ',C&C,LTTA,', 9, NULL, NULL, NULL );
    INSERT INTO @EXPPKG  ( PKG_CODE, PRICE, SEARCH, COUNTY, COMPANY, SRCH_COUNT, UPDT_DT, UPDT_BY, UPDT_CMT ) VALUES ( 'CA-2', 999, 'CO,ER,FC,HB,HI,ST,TX', 'BX,KG,MA,QN,RI', ',CANY,CHATHAM,TRAK,', 7, NULL, NULL, NULL );
    INSERT INTO @EXPPKG  ( PKG_CODE, PRICE, SEARCH, COUNTY, COMPANY, SRCH_COUNT, UPDT_DT, UPDT_BY, UPDT_CMT ) VALUES ( 'CT-4', 999, 'CO,ER,FC,HB', 'BX,KG,MA,QN,RI', ',CLTLTNY,CTALB,CTIM,CTIM-711,CTIM-CC,CTIM-Q,CTIM-R,CTIMR-O,FNT,FNT-A,FNT-AG,FNT-N,FNT-R,NYLS,TICOR,TICORROC,FNT-RAM,', 4, NULL, NULL, NULL );

选择

    select 
            PKG.ID,
            PKG.PKG_CODE,
            PKG.PRICE,
            SRCH.Value AS CNTY,
            CPNY.Value CPNY,
            SRCH.Value AS SRCH,
            PKG.SEARCH,
            PKG.COUNTY,
            PKG.COMPANY,
            PKG.SRCH_COUNT,
            PKG.UPDT_DT,
            PKG.UPDT_BY,
            PKG.UPDT_CMT
             from @EXPPKG PKG
             CROSS APPLY DBO.SplitString(SEARCH, ',') SRCH 
             CROSS APPLY DBO.SplitString(COUNTY, ',') CNTY 
             CROSS APPLY DBO.SplitString(COMPANY, ',') CPNY

结果

【讨论】:

  • SEARCH、COUNTY 和 COMPANY 是 DEV..EXPPKG 中的列。这失败了,因为 SQL 无法识别它们。我相信这是朝着正确的方向前进,但我还没有设法破解代码以使其正常工作。
  • 请添加一些数据示例和功能。我需要重现环境来帮助你。
  • 数据添加到主要问题文本。
  • 检查函数的输出表名。我使用您的数据和通用拆分函数添加了完整的代码。
  • 我会敦促你扔掉那个分离器,开始使用基于集合的分离器。我最喜欢的是杰夫摩登的。 sqlservercentral.com/articles/Tally+Table/72993 这里还有其他几个很好的选择。 sqlperformance.com/2012/07/t-sql-queries/split-strings
猜你喜欢
  • 1970-01-01
  • 2011-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-19
  • 1970-01-01
  • 2016-03-28
  • 1970-01-01
相关资源
最近更新 更多