2016-04-28 16 views
1

中間変数と条件節を使用せずに次のようなやり方が可能かどうか疑問です。countが1より大きい場合にのみ結合を実行する

行なし、1行または複数行のいずれかを含む結果セットを生成する中間クエリを考えてみます。たいていの場合、行は1つだけ生成されますが、複数の行がある場合は結果の行を別の表に結合して、1つまたは複数の行にプルーニングします。この後に、(行がない場合とは異なり)1つの行がある場合は、元の中間クエリによって生成された複数の列を返すことになります。

私は次のようなものがありますが、明らかに機能しません(スイッチの複数の列、結合などはありません)。私が望むのは、現在のSELECT句にあるものを返すことです。@@ROWCOUNT = 1の場合はINNER JOINからAuxilliaryになります。xを1つの行または行なしにプルーンして返します。私はMainAuxilliaryをここではxに複数の行が含まれている場合にのみ検索したくありません。

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 
); 

一時テーブル変数と条件付きのクエリが1つのクエリで可能ですか? 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質問してくれてありがとう!私は実際にメインのIdが必ずしも一意ではないという点で、私の質問の欠陥にも気づいた。ここでの問題は、私が解決する方法を知っていると思う既存のデータベースに状況があることですが、私もその過程で自分自身を教育しようとしています。事は、参加を行うことはほとんどの時間が必要ではなく、パフォーマンスのためにむしろ問題になる可能性があるということです。次に、複数の結果をコードに返すことも問題です(アプリの変更やデータの大量のため)。 – Veksi

答えて

3

結合を使用して、左テーブルの結果セットを減らすことは通常ありません。結果セットを制限するには、代わりにを使用します。別のテーブルと組み合わせると、WHERE [NOT] EXISTSとなります。

それでは、これはあなたのメインクエリであるとしましょう:

select * from main where main.col1 = 1; 

それは、以下のいずれかの結果を返します。そして、私たちは、その後、我々は

  • 1行に行われ、

    • なしの行をdone done
    • 複数行の場合は、where句を拡張する必要があります。

    でクエリ拡張where句以下の結果のいずれかを返し

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

    • ない行、大丈夫です
    • 一列、大丈夫です
    • 複数行の場合 - あなたはこれが不可能だと言う場合

    これは、代わりに1つの手順でこれを行うことです。私はレコードを数え、すべてのレコードについて他のテーブルのマッチを探します。その後...

    • カウントがゼロであれば、我々はそれが1である場合、私はそれが1より大きい場合、私は試合を存在するために行を取り、その行に
    • を取る何の結果とにかく
    • を取得していません一致するものがないときに、他のテーブルまたはnone。ここ

    がいっぱいクエリです:

    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の左右の2つの式の間に優先順位はありません。したがって、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と2つのクエリをテンポラリテーブルを使用する方が良いでしょう。

    関連する問題