簡體   English   中英

子查詢如何引用它之外的表?

[英]How can a sub-query refer to a table outside it?

我試圖了解 JOIN 中的子查詢如何引用上層查詢中的字段。

車輛表存儲了公司使用的車輛的當前信息; 所有車輛歷史都存儲在一個名為 vehicle_aud 的表中,其結構與車輛表完全相同,但還包括對另一個表的引用,稱為修訂,該表存儲有關誰、何時、為什么等對車輛進行更改的信息主表。

為了完成對車輛的最后一個操作,使用了一個非常簡單的 JOIN,如下所示:

SELECT *
FROM vehicles v
    JOIN vehicles_aud vu ON vu.id=v.id AND vu.revision_id=(
        SELECT max(revision_id)
        from vehicles_aud
        WHERE id=v.id
    )
    JOIN revisions r ON r.id=vu.revision_id

請不要介意 SELECT 部分中的星號:我確定在此處指定任何實際字段對於我下面的問題沒有多大意義。 准確地說,這個查詢也可以通過以下方式重寫以便更好地理解:

SELECT *
FROM vehicles v
    CROSS APPLY (
        SELECT TOP 1 *
        FROM vehicles_aud
        WHERE id=v.id
        ORDER BY id DESC
    ) vu
    JOIN revisions r ON r.id=vu.revision_id

在第二個示例中,JOIN 不適用。

我假設第一個示例中的子查詢應該與 CROSS APPLY 運算符一起使用,因為它指的是子查詢之外的車輛表中的 id 字段,但是 IRL 使用上述 JOIN 的查詢效果很好。 我懷疑如果沒有 CROSS APPLY,這怎么可能? 我的意思是,在什么情況下子查詢可以引用子查詢之外的表的字段?

使用分析函數是一種方法:

SELECT TOP 1 WITH TIES *
FROM vehicles v
INNER JOIN vehicles_aud vu ON vu.id = v.id
INNER JOIN revisions r ON r.id = vu.revision_id
ORDER BY ROW_NUMBER() OVER (PARTITION BY v.id ORDER BY vu.revision_id DESC);

上述查詢將返回每組共享相同vehicles.id revision_id值的具有最大revision_id值的所有記錄。

我不確定,這是否真的能回答你的問題......

簡而言之:任何一種 JOIN 都會創建兩個結果集並將它們與給定的條件匹配,而任何一種 APPLY 都會逐行調用操作。 如果 APPLY 返回多行,則添加結果集(類似於 JOIN),而對於單行結果,引擎僅添加列。

實際情況會復雜得多。

該引擎非常智能,會在檢查統計信息、索引、現有計划等后決定最佳計划。 您得到的真正計划很可能不是您所期望的。 對於看似不同的查詢,您獲得的計划很可能是相同的。

在打開“包括實際計划”的情況下嘗試以下操作:

USE master;
GO
CREATE DATABASE testPlan;
GO
USE testPlan;
GO

CREATE TABLE t1 (ID INT IDENTITY CONSTRAINT pk PRIMARY KEY, SomeValue VARCHAR(100));
INSERT INTO t1 VALUES('MaxVal will be 100'),('MaxVal will be 200'),('MaxVal will be 300');
GO

CREATE TABLE t2(fkID INT CONSTRAINT fk FOREIGN KEY REFERENCES t1(ID),TheValue INT);
INSERT INTO t2 VALUES(1,1),(1,2),(1,100)
                    ,(2,1),(2,2),(2,200)
                    ,(3,1),(3,2),(3,300);
GO

--a scalar computation using MAX()
SELECT *
      ,(SELECT MAX(t2.TheValue) FROM t2 WHERE t1.ID=t2.fkID) AS MaxVal
FROM t1

--the same as above, but with APPLY
SELECT *
FROM t1
CROSS APPLY(SELECT MAX(t2.TheValue) FROM t2 WHERE t1.ID=t2.fkID) A(MaxVal)

--Now we pick the TOP 1 after an ORDER BY
SELECT *
      ,(SELECT TOP 1 t2.TheValue FROM t2 WHERE t1.ID=t2.fkID ORDER BY t2.TheValue DESC) AS MaxVal
FROM t1

--and again the same with APPLY
SELECT *
FROM t1
CROSS APPLY(SELECT TOP 1 t2.TheValue FROM t2 WHERE t1.ID=t2.fkID ORDER BY t2.TheValue DESC) A(MaxVal)

--Tim's approach using the very slick TOP 1 WITH TIES approach
SELECT TOP 1 WITH TIES *
FROM t1 INNER JOIN t2 ON t1.ID=t2.fkID
ORDER BY ROW_NUMBER() OVER(PARTITION BY t1.ID ORDER BY t2.TheValue DESC);

GO
USE master;
GO
--carefull with real data!
--DROP DATABASE testPlan;
GO

“標量 MAX”的計划在 27(!) 行上使用表掃描,減少到 9。與 APPLY 相同的方法具有相同的計划。 引擎足夠聰明,可以看到這不需要完全成熟的結果集。 附帶說明:您可以將 MaxVal 用作查詢中的變量,非常有幫助...

在這個小測試中,子查詢中TOP 1的計划是最昂貴的。 開頭和上面一樣(表掃描27行,減少到9行),但是要加一個排序操作。 APPLY 的變化大致相同。

TOP 1 WITH TIES需要 9 行 t2 並對它們進行排序。 以下操作是針對 9 行進行的。 再進行一次排序並減少到 TOP 行。

在這種情況下,第一個是最快的 - 到目前為止。

但是在(您的)現實中,實際行為將取決於現有索引、統計信息和實際行數。 此外,您還有一個額外的級別(多一張桌子)。 查詢越復雜,優化器就越難找到最佳計划。

結論

如果性能很重要,那么就與您的馬匹賽跑並進行測量。 如果性能不是那么重要,請使用更易於閱讀、理解和維護的查詢。

這是您的第一個查詢:

SELECT *
FROM vehicles v JOIN
     vehicles_aud va
     ON va.id = v.id AND
        va.revision_id = (SELECT MAX(va2.revision_id)
                          FROM vehicles_aud va2
                          WHERE va2.id = v.id
--------------------------------^
                         ) JOIN
     revisions r
     ON r.id = va.revision_id;

我想你的問題是關於這個條款的。 這是在相關子查詢相關性子句 表別名的使用闡明了正在發生的事情。

從邏輯上講,發生的情況是,對於外部查詢中的每一行,內部查詢使用va.id的單獨值運行。正如您似乎知道的那樣,它提取了revision_id最新值。

有些人對相關子查詢有一種不自然的偏見,認為數據庫實際上循環遍歷所有行。 請記住,SQL 是一種描述性語言。 盡管這描述了處理正在做什么,但這並不是一般實際發生的情況。 特別是,在某些情況下,相關子查詢可能是有效的機制。

編寫查詢的更“口語化”的方法是使用窗口函數:

SELECT *
FROM vehicles v JOIN
     (SELECT va.*,
             ROW_NUMBER() OVER (PARTITION BY va.id ORDER BY va2.revision_id DESC) as seqnum
      FROM vehicles_aud va
     ) va
     ON va.id = v.id AND
        va.seqnum = 1 JOIN
     revisions r
     ON r.id = va.revision_id;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM