8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

递归 CTE 以及如何克服循环引用错误

user915 2月前

117 0

我有一个用于查找原始位置的脚本。它以这样的表格开始:IDPreviousIDLocation 2NULL1235 321236 431239 8111237 981234 1091235 11101237 ...

我有一个用于查找原始位置的脚本。它以如下表格开始:

ID 以前的ID 地点
2 无效的 1235
3 2 1236
4 3 1239
8 11 1237
9 8 1234
10 9 1235
11 10 1237

我想找到选定数量的 ID 值的原始位置

ID
2
4
8
10
11

我寻找的结果是:

等级 ID 原始ID 地点 原始位置
0 2 2 1235 1235
2 4 2 1239 1235
0 8 8 1237 1237
2 10 8 1235 1237
3 11 8 1237 1237

ID =2 和 =4 的输出很好。但是,对于 8,10,11,这会导致循环引用并且代码中断。

问题:有没有办法解决这个问题以避免发生错误:

语句终止。语句完成前已用尽最大递归次数 100

并产生所需的输出? 我知道 8 是根,因为它是该链中最小的数字,即使该链中存在循环引用。

这是我目前为 ID 2 和 4 生成输出的脚本。如果删除值 8,10,11,它会起作用

DECLARE @IDs TABLE (
  ID INTEGER
  ,PreviousID INTEGER
  ,Location INTEGER
)

INSERT INTO @IDs
SELECT           2,null,1235
UNION ALL SELECT 3,2,1236
UNION ALL SELECT 4,3,1239
UNION ALL SELECT 8,11,1237
UNION ALL SELECT 9,8,1234
UNION ALL SELECT 10,9,1235
UNION ALL SELECT 11,10,1237

Select * from @IDs


DECLARE @ORDERID Table (OrderID nvarchar (100))
Insert into @ORDERID values
('2')
,('4')
--,('8')
--,('10')
--,('11')

;WITH q AS (
    SELECT 0 lvl,  ID, PreviousID,PreviousID LastId
        ,Location,Location as OriginalLocation
    FROM    @IDs
    where ID in (select OrderID from @ORDERID) 
    UNION ALL 
    SELECT lvl+1, q.ID,u.PreviousId,q.PreviousId LastId
       ,q.Location,u.Location
    FROM    q
            INNER JOIN @IDs u ON u.ID = q.PreviousID
            --and q.ID <> u.PreviousID and q.PreviousID <> u.ID
)
select lvl, ID, coalesce(LastId,Id) OriginalId,Location,OriginalLocation 
from q
where PreviousId is null
order by id;

非常感谢您的任何提示或建议。

帖子版权声明 1、本帖标题:递归 CTE 以及如何克服循环引用错误
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由user915在本站《t-sql》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 我有一个服务,其中 SQL Server 数据库中的某个表中有很多插入内容。下面的方法由很多地方和多个线程触发: public async Task InsertLogApi(

    我有一个服务,其中 SQL Server 数据库中的某个表中有很多插入内容。下面的方法由很多地方和多个线程触发:

        public async Task InsertLogApi(LogApi logApi)
        {
            _context.Add(logApi);
            await _context.SaveChangesAsync();
        }
    

    此方法抛出

    SqlException:事务(进程 ID 61)与另一个进程在锁定资源上发生死锁,并已被选为死锁牺牲品。重新运行该事务。

    非常多。

    主键是一种 int 类型,它由数据库自动递增。因此在 SaveChanges EF Core 上有两个请求:

    INSERT INTO [...]
    VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13, @p14, @p15);
    SELECT [Id]
    FROM [...]
    WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();"
    

    我假设当一个线程在 SELECT 上而另一个线程在 INSERT 上时会发生死锁(基于此 question )。我添加了一个事务,方法现在如下所示

    public async Task InsertLogApi(LogApi logApi)
    {
        var s = _context.Database.CreateExecutionStrategy();
    
        await s.ExecuteAsync(async () =>
                {
                    using (var t = _context.Database.BeginTransaction(IsolationLevel.ReadCommitted))
                    {
                        try
                        {
                            _context.Add(logApi);
                            await _context.SaveChangesAsync();
                            await t.CommitAsync();
                        }
                        catch (Exception ex)
                        {
                            await t.RollbackAsync();
                        }
                    }
                });
    }
    

    (我们有重试策略,所以我不得不使用新的策略)。

    这种解决方案有点帮助,我们没有遇到死锁,但性能下降了很多(大约 5 倍)。我试过了 ReadUncommitted ,没用。据我所知,我可以使用客户端生成的 PK(uuid),但现在这不是一个选项。也许我应该对数据库做些什么?

    有没有更好的解决方法,我是否遗漏了什么?

  • 我有一张包含 XML 列的表。我没有该列的数据字典,也没有 XML 结构的定义。大约有 150 万条记录。从对小样本的目视检查来看,

    我有一张包含 XML 列的表。我没有该列的数据字典,也没有 XML 结构的定义。大约有 150 万条记录。从对小样本的目视检查来看,XML 的结构似乎相当相似。我希望能够运行一些 SQL,以便为我提供所有记录的 XML 结构超集。

    该表称为 Remittance ,该列称为 RemittInstr .

    例如,如果我有两个测试数据记录,其 RemittInstr 列中的 XML 值为:

    第 1 行:

    <ri>
        <Msg Type="MT103">
            <AccountNo>12345678</AccountNo>
            <Description code="ORCSR">Ordering Customer<Description>
        </Msg>
    </ri>
    

    第 2 行:

    <ri>
        <Msg Type="MT202">
            <BICFI>ABCD1234</BICFI>
            <Description code="FI">Financial Institution<Description>
        </Msg>
    </ri>
    

    如何编写返回以下行的查询:

    /ri
    /ri/Msg
    /ri/Msg/@Type
    /ri/Msg/AccountNo
    /ri/Msg/BICFI
    /ri/Msg/Description
    /ri/Msg/Description/@code
    

    这样我就能全面了解所有行的 XML 结构了吗?

    编辑:这与链接的问题完全不同,因为链接的问题只处理单个 XML 值。问题是关于在整个表中的行中查找 XML 的结构。解决此问题的解决方案完全不同。@Charlieface 在下面的评论中提供了一个指向良好解决方案的链接。

  • 我正在创建一个存储过程,用于从 SSRS 报告服务器导出 pdf 报告。该报告需要一个名为“ExportId”的参数。该过程运行成功,但它确实...

    我正在创建一个存储过程,用于从 SSRS 报告服务器导出 PDF 报告。该报告需要一个名为“ExportId”的参数。该过程运行成功,但未创建 PDF。我在这里做错了什么?

    
    Alter PROCEDURE rsp_GenerateAndDeliverReports
        @ReportName NVARCHAR(100),
        @Parameter1Name NVARCHAR(100),
        @Parameter1Value NVARCHAR(100),
        @OutputFile NVARCHAR(100)
    AS
    BEGIN
        -- Variables
        DECLARE @executionID AS INT
        DECLARE @reportBytes AS VARBINARY(MAX)
        DECLARE @zipFilePath AS NVARCHAR(100)
        DECLARE @reportService AS INT -- Change the data type to INT
    
        -- Create an instance of the SSRS Execution Service
        EXEC sp_OACreate 'SSRS.ReportExecutionService', @reportService OUT
    
        -- Initialize the service
        EXEC sp_OASetProperty @reportService, 'Url', 'http://localhost/ReportServer/ReportExecution2005.asmx'
    
        -- Load Report 1
        EXEC sp_OAMethod @reportService, 'LoadReport', NULL, @ReportName, NULL, @executionID OUT
    
        -- Set Report 1 Parameters
        EXEC sp_OAMethod @reportService, 'SetExecutionParameters', NULL, @Parameter1Name, @Parameter1Value
    
        -- Render Report 1
        EXEC sp_OAMethod @reportService, 'Render', @reportBytes OUTPUT, 'PDF', NULL, NULL, NULL, NULL, NULL
    
        -- Close and release the service
        EXEC sp_OADestroy @reportService
    
    
    
    
    
        -- Save Report 1 as PDF
        DECLARE @sql NVARCHAR(MAX)
        SET @sql = 'DECLARE @file AS VARBINARY(MAX) 
                    SET @file = ' + CONVERT(NVARCHAR(MAX), @reportBytes, 1) + '
                    EXEC sp_writebytesfile @file, ''' + @OutputFile + ''
        EXEC sp_executesql @sql
    
    END
    
    
    EXEC rsp_GenerateAndDeliverReports @ReportName='/All Reports/Subreports/07_sub', @Parameter1Name ='ExportId', @Parameter1Value='2', @OutputFile='C:\Users\Public\Public Downloads\Report.pdf'
    
    
    
  • SQL Server:假设我有一张包含 Column1 和 Column2 的表。为什么会出现这样的情况:SELECT Column1, * FROM MyTable ORDER BY Column1; 会抛出错误:列名“Column1”不明确但会出现这样的情况:SELECT

    SQL Server:假设我有一张表, Column1 其中 Column2 .

    为什么会这样:

    SELECT Column1, * 
    FROM MyTable 
    ORDER BY Column1;
    

    抛出错误:

    列名‘Column1’不明确

    但是这个:

    SELECT Column1, * 
    FROM MyTable 
    ORDER BY MyTable.Column1;
    

    不会引发错误?

    尝试运行两个命令

  • JRK 2月前 0 只看Ta
    引用 6

    @AlwaysLearning 不,我无法转换它,这就是 EF Core 在 SaveChanges() 上所做的

  • 您可以为此使用递归 CTE。我要警告您,对于 1.5m 行,这可能会非常慢。

    获取属性名称有点复杂,因为似乎无法 .nodes('@*') 获取所有属性。

    相反,您需要 CROSS APPLY 节点名称和其属性的联合。

    WITH cte AS (
        SELECT
          xpath = r.RemittanceInstr.value('local-name(.)','nvarchar(max)'),
          child = r.RemittanceInstr
        FROM Remittance r
    
        UNION ALL
    
        SELECT
          xpath = CONCAT(cte.xpath, '/', v2.name),
          v2.child
        FROM cte
        CROSS APPLY cte.child.nodes('*') x(nd)
        CROSS APPLY (VALUES (x.nd.value('local-name(.)','nvarchar(max)') )) v(name)
        CROSS APPLY (
            SELECT
              v.name,
              x.nd.query('*')
    
            UNION ALL
    
            SELECT
              CONCAT(v.name, '/@', x2.attr.value('local-name(.)','nvarchar(max)')),
              NULL
            FROM x.nd.nodes('@*') x2(attr)
        ) v2(name, child)
    )
    SELECT DISTINCT
      xpath
    FROM cte;
    

    db<>小提琴

  • 要诊断死锁,我们至少需要:死锁的原始 SQL 语句(所有语句,如果在事务中,则为事务中的所有语句),这些语句的查询计划(请通过

  • 您是否在使用快照隔离?此外,此日志条目与其他表之间是否存在任何关系 (FK)?理想情况下,像日志这样随处可见的东西应该具有尽可能少的关系。(理想情况下没有)

  • Moid 2月前 0 只看Ta
    引用 10

    @siggemannen - 是的,我认为这正是我想要的。谢谢。我会将其调整为多行并在此处发布。

  • 引用 11

    @siggemannen - 是的,但无论如何只有一种方法可以找到答案。这将是一个非常有用的通用工具。

  • 改用 SQL 代理作业 和 PowerShell 脚本。实际上,它更简单、更强大。从 TSQL 将报告请求写入表,然后调用 sp_start_job 或安排计划启动代理作业以呈现所有待处理的报告导出请求。

    如果您想了解如何使用 sp_oaxxx 过程,请参见 此处 .

    而且使用 SSRS SOAP API 进行 报告导出 大材小用 URL 访问 ,只需向 URL 发送一个 HTTP GET 即可获得呈现的报告。

  • 听起来生成这个可能需要一段时间,因为你需要递归地解析每一行中的每个节点,你确定这本字典会帮助你,而不只是产生很多噪音吗

  • 不要这么做。这种逻辑到底属于数据库吗?它之所以很难,是因为你不应该这么做。自己编写一个小服务或计划任务。你将拥有更多的控制权,更容易调试,可扩展,最重要的是不会搞砸你的数据库。

  • T-SQL 是一种 SQL 方言,被 Sybase、SQL Server 和 Azure Synapse 等多种产品使用。您在这里使用的 (R)DBMS 产品是什么?

  • SELECT Column1, * FROM MyTable ORDER BY Column1;
    

    有效地

    SELECT Column1, Column1, Column2 FROM MyTable ORDER BY Column1;
    

    因此引擎会感到困惑,不知道 Column1 使用哪个进行排序。

    Query and result

    另一方面,通过编写如下查询

    SELECT Column1, * FROM MyTable ORDER BY MyTable.Column1;
    

    它将按 进行排序 Column1 ,但它知道您指的是 Column1 特定表中的特定名称。想象一下,您要连接两个表,并且它们都有带 Column1 名称的列。引擎无法确定 Column1 您指的是哪个表中的哪个。在您的例子中,很明显它们是相同的,但在其他用例中并非如此,因此出现错误。

    如果您不喜欢使用表名,则可以对列使用不同的名称。

    SELECT Column1 as c1, * FROM MyTable ORDER BY c1;
    

    Query and result

  • 引用 17

    CHARINDEX(CONCAT(',', u.ID), VistiedIDs) AS LoopDetected 不应该是 CHARINDEX(CONCAT(',', u.ID, ','), VistiedIDs) AS LoopDetected 吗?否则 ',5' 可以匹配 ',42,55,9,'。Vistied 是什么意思?

  • @TN,即使你的解决方案没有产生预期的结果,但它很有趣。你的建议对我来说很有价值。不过我还需要一些时间来消化它。谢谢。

  • 虽然我钦佩您对通用解决方案的承诺,但我认为对于这种情况和许多其他情况来说,这有点矫枉过正。SQL 最佳实践更侧重于解决特定问题,而不是像在其他编码语言中那样构建通用查询。而且我很确定我之前已经看到过对此问题的一般解决方案的回答,所以在这种情况下这将是重复的。

  • DTYK 2月前 0 只看Ta
    引用 20

    @DaleK - 说得好。我没听懂。可能需要在过程中跟踪 MIN(ID),并在最终选择中重新加入该行。这样可以处理像 23 -> 21 -> 22 -> 24 -> 20 -> 26 -> 25 这样的奇怪情况,在这些情况下,我们直到到达末尾才知道最小值。

返回
作者最近主题: