[英]Doing a join only if count is greater than one
我不知道下面的一些人為設計的示例是否可以在不使用中間變量和條件子句的情況下實現。
考慮一個中間查詢,該查詢可以生成不包含任何行,一行或多行的結果集。 在大多數情況下,它只產生一行,但是當產生多行時,應該將結果行連接到另一張表,以將其刪減為一行或不行。 在此之后,如果只有一行(而不是沒有行),則需要返回原始中間查詢產生的多列。
我在腦海中有以下類似的想法,但是它顯然不起作用(切換情況下有多個列,沒有聯接等),但是也許可以說明這一點。 我想有是只返回當前什么是在SELECT
子句中的情況下, @@ROWCOUNT = 1
或如果它是更大的,做一個INNER JOIN
到Auxilliary
,其修剪下來x
到一行或無行然后返回。 我不想僅在x
包含多個行的情況下搜索Main
和Auxilliary
。
SELECT x.MainId, x.Data1, x.Data2, x.Data3,
CASE
WHEN @@ROWCOUNT IS NOT NULL AND @@ROWCOUNT = 1 THEN
1
WHEN @@ROWCOUNT IS NOT NULL AND @@ROWCOUNT > 1 THEN
-- Use here @id or MainId to join to Auxilliary and there
-- FilteringCondition = @filteringCondition to prune x to either
-- one or zero rows.
END
FROM
(
SELECT
MainId,
Data1,
Data2,
Data3
FROM Main
WHERE
MainId = @id
) AS x;
CREATE TABLE Main
(
-- This Id may introduce more than row, so it is joined to
-- Auxilliary for further pruning with the given conditions.
MainId INT,
Data1 NVARCHAR(MAX) NOT NULL,
Data2 NVARCHAR(MAX) NOT NULL,
Data3 NVARCHAR(MAX) NOT NULL,
AuxilliaryId INT NOT NULL
);
CREATE TABLE Auxilliary
(
AuxilliaryId INT IDENTITY(1, 1) PRIMARY KEY,
FilteringCondition NVARCHAR(1000) NOT NULL
);
如果沒有一個臨時表變量和一個條件語句,是否可以在一個查詢中實現? 不使用CTE?
一些樣本數據將是
INSERT INTO Auxilliary(FilteringCondition)
VALUES
(N'SomeFilteringCondition1'),
(N'SomeFilteringCondition2'),
(N'SomeFilteringCondition3');
INSERT INTO Main(MainId, Data1, Data2, Data3, AuxilliaryId)
VALUES
(1, N'SomeMainData11', N'SomeMainData12', N'SomeMainData13', 1),
(1, N'SomeMainData21', N'SomeMainData22', N'SomeMainData23', 2),
(2, N'SomeMainData31', N'SomeMainData32', N'SomeMainData33', 3);
還有一個示例查詢,它的行為實際上與我希望的行為類似,僅當使用給定ID直接查詢Main
產生多個結果時,我才希望執行連接。
DECLARE @id AS INT = 1;
DECLARE @filteringCondition AS NVARCHAR(1000) = N'SomeFilteringCondition1';
SELECT *
FROM
Main
INNER JOIN Auxilliary AS aux ON aux.AuxilliaryId = Main.AuxilliaryId
WHERE MainId = @id AND aux.FilteringCondition = @filteringCondition;
您通常不使用聯接來減少左表的結果集。 為了限制結果集,您可以使用where子句 。 結合另一個表,這將是WHERE [NOT] EXISTS
。
因此,假設這是您的主要查詢:
select * from main where main.col1 = 1;
它返回以下結果之一:
帶有擴展where子句的查詢:
select * from main where main.col1 = 1
and exists (select * from other where other.col2 = main.col3);
返回以下結果之一:
因此,任務是一步執行此操作。 我對記錄進行計數,並在每張記錄的另一個表中查找匹配項。 然后 ...
這是完整的查詢:
select *
from
(
select
main.*,
count(*) over () as cnt,
case when exists (select * from other where other.col2 = main.col3) then 1 else 0 end
as other_exists
from main
where main.col1 = 1
) counted_and_checked
where cnt = 1 or other_exists = 1;
更新:我知道您要避免不必要地訪問另一個表。 但是,這很難做到。
為了僅在必要時使用子查詢,我們可以將其移到外部:
select *
from
(
select
main.*,
count(*) over () as cnt
from main
where main.col1 = 1
) counted_and_checked
where cnt = 1 or exists (select * from other where other.col2 = main.col3);
我認為這看起來要好得多。 但是,在OR
左右兩個表達式之間沒有優先級。 因此,DBMS 仍可以在評估cnt = 1
之前對每個記錄執行子選擇。
我知道的使用左到右優先級的唯一操作(即,一旦匹配左側條件就不再看)是COALESCE
。 因此,我們可以執行以下操作:
select *
from
(
select
main.*,
count(*) over () as cnt
from main
where main.col1 = 1
) counted_and_checked
where coalesce( case when cnt = 1 then 1 else null end ,
(select count(*) from other where other.col2 = main.col3)
) > 0;
這可能看起來有些奇怪,但是當cnt為1時,應阻止執行子查詢。
您可以嘗試類似
select * from Main m
where mainId=@id
and @filteringCondition = case when(select count(*) from Main m2 where m2.mainId=@id) >1
then (select filteringCondition from Auxilliary a where a.AuxilliaryId = m.AuxilliaryId) else @filteringCondition end
但這並不是非常快速的查詢。 我最好使用臨時表或只是if
和兩個查詢。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.