【问题标题】:Nesting a WHILE loop within a SELECT在 SELECT 中嵌套 WHILE 循环
【发布时间】:2021-04-09 10:30:42
【问题描述】:

我正在尝试使用 T-SQL 从 EDMS 文档管理系统中提取一些数据。基本上我需要创建一个报告,列出系统上的所有文档以及每个文档的文件路径。 “docs”表中每个以 docid 为键的文档都有一行。这是一个例子:

docid size ext
10001 1 jpg
10002 1 png
10003 1 tif
10004 3 tif

相关文档的文件名通常是 docid 后跟扩展名,但是由于 tif 扫描形式的文档可以跨越多个单独的文件,因此为 tif 文件添加了后缀,该后缀由下划线后跟部分组成number(大小列表示每次扫描存在多少个部分。)因此对于上面的示例,我需要输出以下内容:

docid filepath
10001 c:\documents\10001.jpg
10002 c:\documents\10002.png
10003 c:\documents\10003_1.tif
10004 c:\documents\10004_1.tif
10004 c:\documents\10004_2.tif
10004 c:\documents\10004_3.tif

我认为它需要以某种方式嵌套在 SELECT 中的 WHILE 循环,但不知道如何实现这一点。

【问题讨论】:

  • size 的最大值是多少?此外,WHILE 是您想要的最后 项; SQL 是一种基于集合的语言,因此擅长基于集合的解决方案,但不擅长迭代解决方案。
  • 为什么 10003 有一个 _1 而前两个没有?
  • 我假设 OP 只想为tif 文件、@GordonLinoff 提供零件号。
  • 是的,部件号仅适用于 tif 文件。所以 tif 文件总是有部件号(即使只有一个部件),但其他文件扩展名总是只有一个部件而没有添加后缀。责怪 EDMS 设计者;我不认为我会这样设计...

标签: sql tsql


【解决方案1】:

假设size 的值不能大于10,您可以使用VALUES 表结构来创建额外的行:

SELECT YT.docid,
       CONCAT(N'C:\documents\',YT.docid,CASE YT.ext WHEN 'tif' THEN CONCAT(N'_',V.I) END,N'.',YT.ext) AS FilePath
FROM dbo.YourTable YT
     JOIN (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10))V(I) ON YT.Size >= V.I;

如果您可以有更大的值,那么您可以改为使用内联计数,甚至是实例上现有的计数表。

值得注意的是,我使用 Tally 的原因是因为它们明显比 rCTE 快。即使对于少量的行,使用基于集合的 Tally 方法也比 rCTE 的迭代方法具有性能优势。


似乎 OP 可能有大量数字。因此,正如我所提到的,使用您的实例上的内联 Tally 或 Tally 表。内联计数如下所示:

WITH N AS(
    SELECT N
    FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
    SELECT TOP (SELECT MAX(size) FROM dbo.YourTable)
           ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
    FROM N N1, N N2, N N3) --Up to 1000. Add more cross joins for more rows
SELECT YT.docid,
       CONCAT(N'C:\documents\',YT.docid,CASE YT.ext WHEN 'tif' THEN CONCAT(N'_',T.I) END,N'.',YT.ext) AS FilePath
FROM dbo.YourTable YT
     JOIN Tally T ON YT.Size >= T.I

【讨论】:

  • 我刚刚检查过,不幸的是在某些情况下它会超过一个数字(其中三个分为十个部分,另一个分为十四个部分。)这只是为了我需要的记录提取,它们是整个表的子集;有些远高于十四,但碰巧我对那些不感兴趣。
  • 然后向 VALUES 构造 @IanW 添加更多值。或者,正如我提到的,使用内联 Tally。你从来没有回答我的comment size 的最大值,所以我不得不猜测它不会很大。
  • 我也为你演示了一个内联计数,@IanW。
  • 抱歉回答了别人的问题,但没有专门回答你,抱歉。好的,我现在正在尝试您的内联计数,但我收到“无法绑定多部分标识符“V.I”。”
  • 啊,我在后者中更改了别名,但错过了一个引用。;应该是T.I@IanW。
【解决方案2】:

你可以使用递归 CTE 做你想做的事:

with files as (
      select docid, ext, 1 as cnt, size
      from documents
      union all
      select docid, ext, cnt + 1, size
      from files
      where cnt < size
     )
select f.docid, f.ext, f.cnt,
       concat('c:\documents\', docid,
              (case when ext = 'tif' 
                    then concat('_', cnt)
                    else ''
               end), '.', ext)
from files f;

Here 是一个 dbfiddle。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-02
    • 2020-10-09
    • 2013-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多