SQL Server 每 5 行循环遍历一个表

Posted

技术标签:

【中文标题】SQL Server 每 5 行循环遍历一个表【英文标题】:SQL Server loop through a table for every 5 rows 【发布时间】:2018-12-10 05:28:33 【问题描述】:

我需要编写一个存储过程或表函数来返回一个新的数据表作为新的数据源。 我希望根据发票 ID 列每 5 行循环一次原始表(可能不是从 1 开始),前 5 行添加到新表的左侧,后 5 行添加到右侧新表,左边第三个 5 行,以此类推。

例如,这是原始表格:

这是期望表:

提前致谢!

【问题讨论】:

1112 行呢? 很奇怪,请提出您对此的要求并付出您的努力 @D-Shih 嘿D-Shih,左边11-15,右边16-20等等…… 理想情况下,在 UI/报告构建器中进行这样的 presentation 工作,而不是在数据库中。尽管表面上相似,但表格/结果集与电子表格不同。 我同意 - 这是一个演示练习,不应该在数据库中完成。 【参考方案1】:
declare @rowCount int = 5;
with cte as (
    select *,( (IN_InvoiceID-1) / @rowCount ) % 2 group1
        ,( (IN_InvoiceID-1) / @rowCount ) group2
        ,IN_InvoiceID % @rowCount group3
    from T 
)
select * from cte
select T1.INID,T1.IN_InvoiceID,T1.IN_InvoiceAmount,T2.INID,T2.IN_InvoiceID,T2.IN_InvoiceAmount
from CTE  T1
left join CTE T2 on T2.group1 = 1 and T1.group2 = T2.group2-1 and T1.group3 = T2.group3
where T1.group1 = 0

测试 DDL

CREATE TABLE T
    ([INID] varchar(38), [IN_InvoiceID] int, [IN_InvoiceAmount] int)
;

INSERT INTO T
    ([INID], [IN_InvoiceID], [IN_InvoiceAmount])
VALUES
    ('DB3E17E6-35C5-41:121-93B1-F809BF6B2972', 1, 2999),
    ('3212F048-8213-4FCC-AB64-121485B77D4E43', 2, 3737),
    ('E3526373-A204-40F5-801C-7F8302A4E5E2', 3, 3175),
    ('76CC9C19-BF79-4E8A-8034-A33805AD3390', 4, 391),
    ('EC7A2FBC-B62D-4865-88DE-A8097975F125', 5, 1206),
    ('52AD3046-21331-4F0A-BD1D-67F232C54244', 6, 402),
    ('CA48F132-A9F5-4516-9E58-CDEE6644AAD1', 7, 1996),
    ('02E10C31-CAB2-4220-B66A-CEE5E67A9378', 8, 3906),
    ('98F1EEFF-B07A-4B65-87F4-E165264284DD', 9, 2575),
    ('91EBDD8B-B73C-470C-8900-DD66078483DB', 10, 2965),
    ('6E2490E5-C4DE-4833-877F-1590F7BDC1B8', 11, 1603),
    ('00985921-AC3C-4E3E-BAE1-7F58302F831A', 12, 1302)
;

结果:

【讨论】:

【参考方案2】:

能否请您查看文章 Display Data in Multiple Columns using SQL 以示例案例展示数据库开发人员如何使用 Row_Number() 函数和模式算术表达式以列模式显示数据行列表

您需要从示例中不同的同一行添加其他列

【讨论】:

【参考方案3】:

似乎您想将表拆分为 2 个表,交替 5 行。一个简单的方法是:

    将数据放入具有额外列的临时表中(比如说 grouping_id) 更新分组 ID,使每 5 行具有相同的 ID。你可以 使用in_invoiceId % 5(点头功能)。在这一步之后,前 5 行将有 grouping_id 0,接下来 5 将有 1,下一个将有 2 (假设所有行的发票 ID 都增加了 +1)。 您可以使用 where 子句对奇数和偶数 grouping_id 进行普通选择

【讨论】:

【参考方案4】:

理想情况下,您可以使用 2 个表主表和详细表进行管理。

但由于我的好奇心,我能够解决并给出答案

Declare @table table(id int identity, invoice_id int)

; WITH Numbers AS
(
    SELECT n = 1
    UNION ALL
    SELECT n + 1
    FROM Numbers
    WHERE n+1 <= 50
)


insert into @table  SELECT n
FROM Numbers

Select (a.id  )%5 ,* from @table a join @table b on a.id+5 = b.id and a.id != b.id

;WITH Numbers AS
(
    SELECT n = 1, o = 5
    UNION ALL
    SELECT n + 10, o = o+10
    FROM Numbers
    WHERE n+1 <= 50
)

select a.id ParentId,a.invoice_id ParentInvoiceId, --b.n, b.o, 
c.invoice_id childInvoiceID from @table a 
join Numbers b on a.id between b.n and b.o
left join @table c on a.id + 5 = c.id

【讨论】:

【参考方案5】:

这是我的解决方案

首先,我根据 in_invoiceid 是否可被 5 整除来创建 grps。(忽略余数)

之后我创建一个类别来指示替代组之间(即通过检查余数是否为 0 或其他)

然后是根据in_invoiceid排序的类别字段对记录进行dense_ranking的问题

最后是 category=1 行的 join,其 dense_rank 与 category=0 中的记录相同

create table Invoicetable(IN_ID varchar(100), IN_InvoiceID int)


INSERT INTO Invoicetable (IN_ID, IN_InvoiceID)
VALUES
    ('2345-BCDE-6645-1DDF', 1),
    ('2345-BCDE-6645-3DDF', 2),
    ('2345-BCDE-6645-4DDF', 3),
    ('2345-BCDE-6645-5DDF', 4),
    ('2345-BCDE-6645-6DDF', 5),
    ('2345-BCDE-6645-7DDF', 6),
    ('2345-BCDE-6645-aDDF', 7),
    ('2345-BCDE-6645-sDDF', 8),
    ('2345-BCDE-6645-dDDF', 9),
    ('2345-BCDE-6645-dDDF', 10),
    ('2345-BCDE-6645-dDDF', 11),
    ('2345-BCDE-6645-dDDF', 12);

with data
  as (
select *
      ,(in_invoiceid-1)/5 as grp
      ,case when ((in_invoiceid-1)/5)%2=0 then '1' else '0' end as category
      ,dense_rank() over(partition by  case when ((in_invoiceid-1)/5)%2=0 then '1' else '0' end
                    order by in_invoiceid) as rnk
  from invoicetable a
      )
   select *
      from data a
 left join data b
        on a.rnk=b.rnk       
       and b.category=0
     where a.category=1   

这里是 db fiddle 链接。

https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=287f101737c580ca271940764b2536ae

【讨论】:

【参考方案6】:

您可以尝试以下方法。使用(((ROW_NUMBER() OVER (ORDER BY IN_InvoiceID) - 1) / 5) % 2 = 0) 将表格划分为左右组。

CREATE TABLE #InvoiceTable(
    IN_ID varchar(24),
    IN_InvoiceID int
)


INSERT INTO #InvoiceTable (IN_ID, IN_InvoiceID)
VALUES
    ('2345-BCDE-6645-1DDF', 1),
    ('2345-BCDE-6645-3DDF', 2),
    ('2345-BCDE-6645-4DDF', 3),
    ('2345-BCDE-6645-5DDF', 4),
    ('2345-BCDE-6645-6DDF', 5),
    ('2345-BCDE-6645-7DDF', 6),
    ('2345-BCDE-6645-aDDF', 7),
    ('2345-BCDE-6645-sDDF', 8),
    ('2345-BCDE-6645-dDDF', 9),
    ('2345-BCDE-6645-dDDF', 10),
    ('2345-BCDE-6645-dDDF', 11),
    ('2345-BCDE-6645-dDDF', 12);

WITH cte AS (
    SELECT 
        IN_ID, 
        IN_InvoiceID, 
        CASE
            WHEN (((ROW_NUMBER() OVER (ORDER BY IN_InvoiceID) - 1) / 5) % 2 = 0) THEN 'L'
            ELSE 'R'
        END AS IN_Position
    FROM #InvoiceTable
),
cteL AS (
    SELECT IN_ID, IN_InvoiceID, ROW_NUMBER() OVER (ORDER BY IN_InvoiceID) AS IN_RowNumber
    FROM cte
    WHERE IN_Position = 'L'
),
cteR AS (
    SELECT IN_ID, IN_InvoiceID, ROW_NUMBER() OVER (ORDER BY IN_InvoiceID) AS IN_RowNumber
    FROM cte
    WHERE IN_Position = 'R'
)

SELECT cteL.IN_ID, cteL.IN_InvoiceID, cteR.IN_ID, cteR.IN_InvoiceID
FROM cteL
LEFT JOIN cteR ON (cteL.IN_RowNumber = cteR.IN_RowNumber)

输出:

IN_ID               IN_InvoiceID    IN_ID               IN_InvoiceID
2345-BCDE-6645-1DDF 1               2345-BCDE-6645-7DDF 6
2345-BCDE-6645-3DDF 2               2345-BCDE-6645-aDDF 7
2345-BCDE-6645-4DDF 3               2345-BCDE-6645-sDDF 8
2345-BCDE-6645-5DDF 4               2345-BCDE-6645-dDDF 9
2345-BCDE-6645-6DDF 5               2345-BCDE-6645-dDDF 10
2345-BCDE-6645-dDDF 11              NULL                NULL
2345-BCDE-6645-dDDF 12              NULL                NULL

【讨论】:

以上是关于SQL Server 每 5 行循环遍历一个表的主要内容,如果未能解决你的问题,请参考以下文章

从 SQL Server 表中选择 n 个随机行

SQL SERVER循环遍历(普通循环和游标循环)

使用每一行作为 Insert Into 循环遍历表的行 - SQL

SQL Server 2008 中的 While 循环遍历日期范围,然后插入

sql 游标如何循环

动态 SQL Server 查询循环遍历架构查找主键重复