簡體   English   中英

僅在計數大於1時進行聯接

[英]Doing a join only if count is greater than one

我不知道下面的一些人為設計的示例是否可以在不使用中間變量和條件子句的情況下實現。

考慮一個中間查詢,該查詢可以生成不包含任何行,一行或多行的結果集。 在大多數情況下,它只產生一行,但是當產生多行時,應該將結果行連接到另一張表,以將其刪減為一行或不行。 在此之后,如果只有一行(而不是沒有行),則需要返回原始中間查詢產生的多列。

我在腦海中有以下類似的想法,但是它顯然不起作用(切換情況下有多個列,沒有聯接等),但是也許可以說明這一點。 我想有是只返回當前什么是在SELECT子句中的情況下, @@ROWCOUNT = 1或如果它是更大的,做一個INNER JOINAuxilliary ,其修剪下來x到一行或無行然后返回。 我不想僅在x包含多個行的情況下搜索MainAuxilliary

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子句

帶有擴展where子句的查詢:

select * from main where main.col1 = 1
and exists (select * from other where other.col2 = main.col3);

返回以下結果之一:

  • 沒有行,沒關系
  • 一排,還可以
  • 多行 -您說這不可能

因此,任務是一步執行此操作。 我對記錄進行計數,並在每張記錄的另一個表中查找匹配項。 然后 ...

  • 如果計數為零,我們還是沒有結果
  • 如果是那一個,我就去那排
  • 如果大於1,則我認為另一表中存在匹配項的行,或者當不存在匹配項時不進行匹配的行

這是完整的查詢:

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.

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