【问题标题】:TSQL how do you iterate through rows while parsing them?TSQL 在解析行时如何迭代它们?
【发布时间】:2023-03-08 00:20:01
【问题描述】:

抱歉,我不知道如何描述这个问题的措辞不佳。我想遍历表中的每一行,并在这样做的同时提取一列,解析其中的 varchar,并根据它找到的内容将行插入另一个表。类似这样的东西:

DECLARE @string varchar(max);
foreach row in (select * from Table) {
    set @string = row[column];
    while (len(@string) > 0) {
        -- Do all the parsing in here

        if (found what was looking for)
            insert into Table2 values(row[column2], row[column3]);
    }
}

如果这是一个存储过程,那么它可以在 SQL 中完成,这将是非常好的。我只是不太确定如何处理它。谢谢。

编辑:

这基本上是我希望的功能:

Table 1   |
id_number | text    |
1           Hello, test 532. Yay oh and test 111   
2           test 932.
3           This is a test 315 of stuff test 555.
4           haflksdhfal test 311 sadjhfalsd
5           Yay.

我想通过此表并解析所有文本列以查找“test #”的实例,其中 # 是一个数字。当它在该格式的文本中找到某些内容时,它将将该值插入到另一个表中,例如:

Table 2   |
id_number | number
1           532
1           111
2           932
3           315
3           555
4           311

【问题讨论】:

  • +1:感谢更新 - 我可以看到正则表达式非常有用,但 SQL Server 没有原生正则表达式支持(PATINDEX 支持有限)。要获得正则表达式支持,SQL Server 2005+ 要求您使用 CLR 函数/过程。从单行中提取多个值并不好,但使用游标会很痛苦——你必须知道你所在的行,如果是第 1 行和第 3 行——你离开的地方。
  • 数字是3位数吗?
  • @OMG Ponies - 不,它们可以是任意数字,甚至不必采用那种格式,它们可以是 test - 5162 test 350 test as392 我想通过并尝试如果我找到测试,然后尝试在附近提取一个数字,所以如果有错误输入,它足够聪明地找到它。我可以在 C# 中很好地做到这一点,这正是我现在正在使用的,但最好让一个存储过程来完成所有这些工作。
  • 您使用的是什么版本的 SQL Server?
  • 这样的问题通常表明您需要更好的数据结构设计。如果您有一个名为 test 的相关表,并将其中的值与原始表的 FK 一起存储,那么查询起来会容易得多。现在,如果您收到这样的数据,我可以看到使用所描述的技术将其解析为正确的规范化设计。否则,请修复您的设计。此外,在数据库中,通过交互来解决问题通常不是一个好主意。数据库在数据集上工作得最好,迭代一次只工作一行,因此通常是最慢的解决方案。

标签: sql sql-server tsql stored-procedures parsing


【解决方案1】:

你是在程序上思考而不是基于集合。您可能可以将整个内容编写为单个查询:

INSERT INTO target_table (column list)
SELECT (column list)
FROM source_table
WHERE (parse your column) = (some criterion)

它更容易编写,而且可能也更快。

如果你的解析函数比较复杂,你可以将其放入用户定义的函数中,而不是直接嵌入到查询中。

【讨论】:

  • 解析标准有点太高级了,可以包含很多不同的东西,所以 where 部分不起作用,但感谢您的建议。
  • @Bob L:从表面上看,你想要的是一个光标。但是 SQL 是基于 SET 的,并且取决于解析标准 - 在 Mark 的答案中能够工作。我发现一个非常持久的问题,人们认为他们处于过程/OO编程领域,以及他们努力将一些有效的东西拼凑在一起......而它的表现非常糟糕。细节,即使是抽象的,也有利于我们帮助您实现目标。也许您只需要一个光标 - 在我投票给某人的答案之前,我想更多地了解您的需求。
  • 我明白你的意思。我添加了一些关于我想要实现的目标的信息。
  • +1:基于更新,假设 CLR 可用且可以使用 - 适合这种情况的最佳选择 IMO。
  • @OMG Ponies - 正如您从编辑中看到的那样,如果情况属实,它比 1-1 插入要复杂一些。这就是为什么我不知道如何处理它。
【解决方案2】:

在 SQL Server 2008 中,您可以这样做

WITH testTable AS
(
SELECT 1 AS id_number, N'Hello, test 532. Yay oh and test 111' AS txt UNION ALL
SELECT 2, N'test 932.' UNION ALL
SELECT 3, N'This is a test 315 of stuff test 555.' UNION ALL
SELECT 4, N'haflksdhfal test 311 sadjhfalsd' UNION ALL
SELECT 5, N'Yay.'
)

SELECT id_number,display_term
FROM testTable
CROSS APPLY sys.dm_fts_parser('"' + REPLACE(txt,'"','""') + '"', 1033, 0,0)
WHERE TXT IS NOT NULL and 
  display_term NOT LIKE '%[^0-9]%' /*Or use LIKE '[0-9][0-9][0-9]' to only get 3 
                                     digit numbers*/

返回

id_number   display_term
----------- ------------------------------
1           532
1           111
2           932
3           315
3           555
4           311

【讨论】:

  • 我明白了,我在上面的评论中提到过,我给出的示例是最前卫的示例,文本中可以包含更多内容,例如:'Hello 31 this is a test ver412 of manliness 。或 'Test-135 yay' 或 '351 31 test 31 392' 这将是 31。
  • 好吧,这实际上更接近我想要完成的目标,但是,我想我可以解决剩下的标准并编辑你显示的内容(我猜只是通过链接 OR 来尝试并涵盖各种可能性。谢谢!
  • @Bob - 我认为 ver412 肯定会出错(如果想要的结果是应该提取 412?)。我认为@gbn's 应该可以应付这种情况。
  • 嗯,看着他的回复,我不确定它如何应对所有数据输入的变体。我认为这个就足够了,让它能够通过错误的输入来尝试理解它会很好,但如果输入是错误的,我想我会让它保持错误。再次感谢您的建议。
【解决方案3】:

这样的事情是你总是有“测试(数字)”。它适用于 SQL Server 2005+

DECLARE @Table1 TABLE (id_number int, textcol nvarchar(MAX))

INSERT @Table1 VALUES (1, 'Hello, test 532. Yay oh and test 111')
INSERT @Table1 VALUES (2, 'test 932.')
INSERT @Table1 VALUES (3, 'This is a test 315 of stuff test 555.')
INSERT @Table1 VALUES (4, 'haflksdhfal test 311 sadjhfalsd')
INSERT @Table1 VALUES (5, 'Yay.')


;WITH cte AS
(
    SELECT TOP 9999 CAST(ROW_NUMBER() OVER (ORDER BY c1.OBJECT_ID) AS varchar(6)) AS TestNum
    FROM sys.columns c1 CROSS JOIN sys.columns c2
)
SELECT id_number, TestNum FROM
    cte
    JOIN
    @Table1 ON PATINDEX('%Test ' + TestNum + '[^0-9]%', textcol) > 0
                    OR textcol LIKE '%Test ' + TestNum
ORDER BY
    id_number

【讨论】:

    【解决方案4】:

    您正在寻找的功能称为CURSOR - here 是一篇关于如何使用它们的文章。

    它们被认为不利于性能并且难以正确使用。

    重新思考您的问题并重述它,以便可以在基于集合的操作中解决它。

    考虑使用table variablessub queries 来解决您的复杂情况。

    【讨论】:

    • 哦,谢谢!除了一些相对中间的存储过程之外,我对编写更高级的 SQL 语句有点陌生。
    • 嗯,好的。所以你认为没有游标有更好的方法吗?
    • @Bob L - 我敦促您考虑不需要游标的解决方案。临时表和子查询可以帮助解决困难的 where 条件。查找这两个术语。
    • @Oded - 问题是像 Mark Byers 提到的那样做会很好,但是解析很困难。我想检查存储在列中的一段,例如:您好,测试 521,测试 431,结束。并将 521 和 431 插入到另一个表中。根据我对游标的了解,这可能是唯一的解决方案。
    • @Bob L - 使用表变量。您可以在其中插入 id 和数据,解析和更新它并使用结果。正如@Mark Byers 所说,你被困在程序上而不是基于集合的思考。
    【解决方案5】:

    您正在寻找光标 - 请参阅 MSDN 文档 here。请注意,应尽可能避免使用游标 - 适合它们的地方很少,并且可能导致代码效率低下 - 通常最好尝试基于集合的解决方案。

    【讨论】:

      【解决方案6】:

      要按照您的要求执行此操作,您可以使用光标进行迭代,使用下面的示例信息是光标的布局方式。您将逐行流程放在我的评论所在的位置。

      DECLARE @CurrentRecord VARCHAR(MAX)
      
      DECLARE db_cursor CURSOR FOR  
      SELECT Column
      FROM Table 
      
      OPEN db_cursor   
      FETCH NEXT FROM db_cursor INTO @CurrentRecord
      
      WHILE @@FETCH_STATUS = 0   
      BEGIN   
             --Your stuff here
      
             FETCH NEXT FROM db_cursor INTO @name   
      END   
      
      CLOSE db_cursor   
      DEALLOCATE db_cursor
      

      但是,这取决于您正在做什么,以及这是否是您经常做的事情。我建议您查看是否可以将解析提取到用户定义的函数中,然后您可以使其基于设置,而不是使用游标。作为光标应该是“最后一搏”的努力。

      【讨论】:

        猜你喜欢
        • 2012-06-15
        • 2022-09-23
        • 2020-12-20
        • 2013-08-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-24
        • 2011-06-03
        相关资源
        最近更新 更多