【问题标题】:Loop in SQL and increase one field each timeSQL循环,每次增加一个字段
【发布时间】:2020-12-15 07:18:26
【问题描述】:

为存储原始数据创建表:

CREATE TABLE [dbo].[raw_data](
[ID] [int] IDENTITY(1,1) NOT NULL,
[first_data] [int] NULL,
[next_counts] [nvarchar](max) NULL,
PRIMARY KEY (ID) );

为处理后的数据创建表:

CREATE TABLE [dbo].[SplittedAndProcessedData](
[ID] [int] IDENTITY(1,1) NOT NULL,
[count] [int] NULL,
PRIMARY KEY (ID) );

现在存储原始数据:

INSERT INTO [dbo].[raw_data]
(
     [raw_data].[first_data]
    ,[raw_data].[next_counts]
) VALUES
(
    8500   /*first_data*/
    ,'10,2,0,95,15' /*next_counts*/
)

我正在将逗号分隔的字符串转换为单独的行:

SELECT Splitted_Row.value
FROM [dbo].[raw_data]
CROSS APPLY STRING_SPLIT ([raw_data].[next_counts], ',') Splitted_Row

像这样:

value
10
2
0
95
15

我想使用循环插入SplittedAndProcessedData 表,如下所示:

ID count
1 8500
2 8510
3 8512
4 8512
5 8607
6 8622

这意味着:

  1. ID(1) = 8500 --第一个数据
  2. ID(2) = 8500 + 10 = 8510
  3. ID(3) = 8510 + 2 = 8512
  4. ID(4) = 8512 + 0 = 8512
  5. ID(5) = 8512 + 95 = 8607
  6. ID(6) = 8607 + 15 = 8622

【问题讨论】:

  • 在预期结果中,您希望如何排序数据?
  • SQL 是一种面向集合的语言。它不需要循环。在这种情况下,您所描述的是基于一些随机顺序的运行总计。真正的解决方案是将 CSV 存储在一个字段中。这是一个重大的设计错误,它打破了最基本的数据库规则——一个单元格存储一个值
  • 您无法获得正确的顺序,请参阅 STRING_SPLIT,其中说:“输出行可能是任何顺序”
  • 您可以使用OVER 子句来计算运行总计,例如SUM(value) OVER (ORDER By ???),但您必须以某种方式指定值的顺序。在 SQL 中,除非由 ORDER BY 指定,否则没有隐式顺序。如果您不关心可能会更改订单,您可以使用ORDER BY NULL
  • 在保存原始数据时,我们也可以通过SCOPE_IDENTITY在SplittedAndProcessedData表中插入处理后的数据

标签: sql sql-server tsql


【解决方案1】:

您需要一个累积和,但使用STRING_SPLIT() 按位置提取子字符串是一个棘手的方法。正如documentation 中提到的那样,输出行可能是任何顺序,并且不能保证该顺序与输入字符串中子字符串的顺序相匹配

但您可以尝试使用基于 JSON 的方法来解析 next_counts 列。您需要将数据转换为有效的 JSON 数组(10,2,0,95,15 转换为 [10,2,0,95,15])并使用 OPENJSON() 解析该数组。 OPENJSON() 调用的结果是一个包含 keyvaluetype 列的表,key 列保存指定数组中元素的从 0 开始的索引。

-- INSERT INTO SplittedAndProcessedData ([count])
SELECT 
   SUM(data) OVER (ORDER BY rn) AS [count]
FROM (
   SELECT -1 AS rn, first_data AS data
   FROM raw_data r
   UNION ALL
   SELECT CONVERT(int, j.[key]), TRY_CONVERT(int, j.value)
   FROM raw_data r
   CROSS APPLY OPENJSON(CONCAT('[', r.next_counts, ']')) j
) t  

结果(形成语句的SELECT 部分):

count
-----
8500
8510
8512
8512
8607
8622

【讨论】:

    【解决方案2】:

    可以使用解析函数sum如下:

    SELECT [raw_data].[firstdata] 
           + sum(Splitted_Row.value) 
              over (partition by [raw_data].[id] 
                   order by CHARINDEX(',' + Splitted_Row.value + ',', ',' + [raw_data].[next_counts] + ',')
    FROM [dbo].[raw_data]
    CROSS APPLY STRING_SPLIT ([raw_data].[next_counts], ',') Splitted_Row
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-12-08
      • 2022-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-24
      • 2011-12-01
      相关资源
      最近更新 更多