SQL Server TSQL在同一个表上合并多个子查询

xt0899hw  于 2022-11-28  发布在  其他
关注(0)|答案(2)|浏览(135)

我一直在尝试改进一个SQL查询,它在同一个表上使用多个子查询,但条件不同,并且只检索每个子查询的第一个结果。
我将尝试简化用例:
我有一个表Products,如下所示:
| 产品标识(_I)|参考文献|字段3|字段4|
| - -|- -|- -|- -|
| 一个|参考1|值1| val 3值|
| 2个|参考2|值2| val 4值|
还有另一个表History
| 历史记录标识(_I)|参考文献|实用代码|物理码|发行人|媒介|日期时间|
| - -|- -|- -|- -|- -|- -|- -|
| 一个|参考1| '测试'|'油管测试'|'0个'|'音频'|'日期'|
| 2个|参考2| '电话'|'呼叫方'|'一'|'视频(& V)'|'日期'|
| 三个|参考2| '测试'|'呼叫方'|'二'|'测试(& T)'|'日期'|
History是一个日志表,因此包含很多值。
现在我有一个这样的查询

SELECT 
    p.reference,
    p.field3, p.field4,
    (SELECT TOP 1 a_date 
     FROM history h 
     WHERE h.reference = p.reference 
       AND physicalcode = 'TST' 
       AND issue = 0 
     ORDER BY a_date DESC) AS latest_date_issue_0,
    (SELECT TOP 1 a_date 
     FROM history h 
     WHERE h.reference = p.reference 
       AND physicalcode = 'TST' 
       AND issue = 1 
     ORDER BY a_date DESC) AS latest_date_issue_1
    (SELECT TOP 1 a_date 
     FROM history h 
     WHERE h.reference = p.reference 
       AND utilcode = 'phone' 
     ORDER BY a_date DESC) AS latest_date_phone,
    (SELECT TOP 1 media 
     FROM history h 
     WHERE h.reference = p.reference 
       AND utilcode = 'phone' 
     ORDER BY a_date DESC) AS latest_media,
    -- and so on with many possible combinations
    -- Note that there are more than this few fields on the tables I work on.
WHERE
    p.field3 = 'valX',
    p.field4 = 'valY'
FROM
    products p

我如何合并每个子选择?甚至是几个相似的来提高性能?
历史记录是一个非常大的表,多次选择它会大大降低查询速度。
主要的问题是我每次只需要第一个值。
谢谢你的时间,我希望找到一个更好的方法来处理这个问题!
我尝试使用ROW_NUMBER(),但找不到合适的使用方法。
我还尝试使用WITH创建一个临时表,将历史记录中的每一种可能性分组,但情况更糟。
编辑:执行计划https://www.brentozar.com/pastetheplan/?id=Sy1AKIsUs

qni6mghb

qni6mghb1#

您可以将相关的子查询(您称之为“子选择”)转换为独立的子查询,然后对它们进行JOIN操作。这样,每个子查询只需运行一次。我将为您演示如何对第一个子查询执行此操作。
下面是替换第一个子查询的子查询。

SELECT reference, MAX(a_date) a_date
FROM history
WHERE  physicalcode = 'TST' 
AND issue = 0
GROUP BY reference

这将提供一个虚拟表,其中包含历史表中与问题中的条件匹配的每个引用编号的最新日期。
然后,您可以将其连接到主表,如下所示:

SELECT 
    p.reference,
    p.field3, p.field4,
    a.a_date a_date_issue_0
FROM products p
LEFT JOIN ( /*the subquery */
           SELECT reference, MAX(a_date) a_date
           FROM history
           WHERE  physicalcode = 'TST' 
           AND issue = 0
           GROUP BY reference
   ) a ON p.reference=a.reference

这些子查询也可以定义为VIEW或公用表表达式(CTE)。如果您有许多这样的子查询,您可能会发现这样做更容易阅读和推理查询。
最后一个子查询用这种方法处理有点麻烦。我建议你先处理这个答案,然后再问另一个问题。

ssm49v7z

ssm49v7z2#

感谢@O.Jones,我找到了改进此查询的方法。
为了合并几个请求,我使用了如下CTE:
起始日期

SELECT
(SELECT TOP 1 a_date 
     FROM history h 
     WHERE h.reference = p.reference 
       AND physicalcode = 'TST' 
       AND issue = 0 
     ORDER BY a_date DESC) AS latest_date_issue_0,
    (SELECT TOP 1 a_date 
     FROM history h 
     WHERE h.reference = p.reference 
       AND physicalcode = 'TST' 
       AND issue = 1 
     ORDER BY a_date DESC) AS latest_date_issue_1
     (SELECT top 1 a_date 
      FROM history h 
      WHERE h.reference = p.reference 
        AND h.physicalcode = 'TSTKO' 
      ORDER BY h.d_systeme DESC ) AS d_tst_ko,
     (SELECT top 1 a_date 
       FROM history h 
       WHERE h.reference = p.reference 
         AND h.physicalcode = 'CALLERID' 
       ORDER BY h.d_systeme DESC ) AS d_wrong_number
FROM products p

结束日期

WITH physicalcode_cte (reference, physicalcode, issue, a_date)  as
    (
        SELECT reference, physicalcode, issue, max(a_date)
        from historique
        where codephysique in ('TST','TSTKO','CALLERID')
        and a_date > dateadd(month, -4, getdate()) -- filter on date range to reduce number of rows
        group by reference, physicalcode, issue
    )
SELECT
date_issue_0.a_date,
date_issue_1.a_date,
tst_ko.a_date,
wrong_number.a_date
FROM products p
LEFT JOIN physicalcode_cte date_issue_0 on p.reference = date_issue_0.reference 
        AND date_issue_0.codephysique = 'TST' 
        AND date_issue_0.anomalie = 0
LEFT JOIN physicalcode_cte date_issue_1 on p.reference = date_issue_1.reference
        AND date_issue_1.codephysique = 'TST' 
        AND date_issue_1.anomalie = 1
LEFT JOIN physicalcode_cte tst_ko on p.reference = tst_ko.reference
        AND tst_ko.codephysique = 'TST' 
LEFT JOIN physicalcode_cte wrong_number on p.reference = wrong_number.reference AND 
        AND wrong_number.codephysique = 'TST'

我已经将这个想法应用到不同的场景中,并制作了2个CTE。我不能合并所有的东西,有时候合并会导致成本增加。但是经过几次测试,我已经能够从7100的总成本增加到2100。
它仍然很多,但无论如何少了3倍。需要5秒钟而不是超时。
这是一个用于每月报告的查询,所以我不需要它是超级快,我会保持这种方式。
谢谢你!

相关问题