2016-04-28 60 views
1

我不知道如果沒有使用中間變量和條件子句,下面的一些設計實例是否可能。只有在計數大於1的情況下才進行連接

考慮一箇中間查詢,它可以生成一個結果集,其中不包含行,一行或多行。大多數情況下,它只產生一行,但是當多行時,應該將結果行連接到另一個表,以將其修剪爲一行或無行。在此之後,如果有一行(而不是沒有行),則想要返回由原始中間查詢產生的多列。

我在腦海裏想了一些像下面這樣的東西,但它不會明顯的工作(在開關箱中有多列,沒有連接等),但也許它說明了這一點。我想要的只是在@@ROWCOUNT = 1的情況下返回SELECT子句中的當前條件,或者如果它更大,請執行INNER JOINAuxilliary,這會將x修剪爲一行或無行,然後返回該行。我不想多次搜索Main,只有當x包含多行時,纔會搜索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); 

和樣本查詢,這實際上表現爲我想它需要提醒的表現我想要做的只能加入,如果直接與查詢Main給定ID會產生多個結果。

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; 
+3

你能編輯的問題,並提供樣本數據和預期的結果。我確信這可以通過一個查詢來完成,但很難確切知道你正在做什麼。 –

+0

當然可以!我會嘗試儘快(足夠)! – Veksi

+0

@GordonLinoff感謝您的詢問!我實際上也注意到我的問題也有一個缺陷,那就是Main in Id不一定是唯一的。這裏的事情是我在現有的數據庫中有一種情況,我想我知道如何解決,但我也試圖在這個過程中教育自己。事情是,大部分時間不需要進行連接,並且由於性能可能會造成問題。然後將多個結果返回給代碼也有問題(由於應用程序更改和大量數據)。 – Veksi

回答

3

您通常不會使用連接來減少左表的結果集。要限制結果集,您應該使用代替。結合另一張表,這將是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); 

它返回下列結果之一:

  • 沒有行,這是好的
  • 一行,這是好的
  • 不止一行 - 你說這是不可能的

所以,我們的任務就是改變這一步。我計算記錄並在其他表格中爲每條記錄查找匹配項。然後......

  • 如果計數爲零,我們得到任何結果反正
  • ,如果它是一個我採取這一行
  • ,如果它是大於一個,我承擔其是否存在匹配的行在其他表或無時沒有匹配

以下是完整的查詢:

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的左右兩個表達式中沒有優先級。因此,在評估cnt = 1之前,DBMS可能會仍然在每條記錄上執行子查詢。

我知道使用從左到右的優先順序的唯一操作,即一旦左側的條件匹配,看起來沒有進一步的是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

+0

這不會總是查詢'other'嗎?我想避免連接,除非'main.col1 = 1'返回多於一行。不過,我喜歡你的筆記,關於一般加入。 – Veksi

+0

是的。這很難規避。請參閱我的更新。 –

+0

謝謝!你在解釋這件事上做了很多工作。它看起來像把結果集放到一個表變量中,並且有一個IF條件是最簡單的方法。 – Veksi

1

您可以嘗試像

select * from Main m 
where [email protected] 
and @filteringCondition = case when(select count(*) from Main m2 where [email protected]) >1 
then (select filteringCondition from Auxilliary a where a.AuxilliaryId = m.AuxilliaryId) else @filteringCondition end 

,但它幾乎沒有非常快速的查詢。我最好使用臨時表或只有if和兩個查詢。

相關問題