sql接続クエリにおいて、whereキーワードの位置説明

3829 ワード

筆者は生まれつき不器用で、しかも思考が厳格ではないので、sql文を書くのは本当に苦手です。達人は笑わないでください。直接に本文を飛ばしてください。
背景は多く紹介しないで、先に表を作って、テストデータを挿入しましょう。フィールドのコメントがあります。

--
CREATE TABLE doctor
    (
      id INT IDENTITY(1, 1) , --ID
      docNumber NVARCHAR(50) NOT NULL , --
      NAME NVARCHAR(50) NOT NULL   --
    )
go

--
INSERT  INTO doctor
VALUES  ( '007', 'Tom' )
INSERT  INTO doctor
VALUES  ( '008', 'John' )
INSERT  INTO doctor
VALUES  ( '009', 'Jim' )


-- ( )
CREATE TABLE Nosource
    (
      id INT IDENTITY(1, 1) ,
      docNumber NVARCHAR(50) NOT NULL , --
      workTime DATETIME NOT NULL
    )

go
--
INSERT  INTO Nosource
VALUES  ( '007', '20120819' )
INSERT  INTO Nosource
VALUES  ( '007', '20120820' )
INSERT  INTO Nosource
VALUES  ( '007', '20120821' )
INSERT  INTO Nosource
VALUES  ( '008', '20120821' )

表が作成されたら、テストデータもOKです。これから需要です。
1.医師一人に関する情報とその医師が持っている番号源の数を調べてください。
これは簡単すぎます。ハロルドやデータベースの基礎を少し学んだばかりの友達でさえ、心が痛いかもしれません。でもコードはやはり書きます。

--
SELECT  COUNT(nos.id) AS PersonNumSounceCOUNT , --
        dct.ID AS docID ,
        dct.NAME ,
        dct.docNumber ,
        nos.workTime
FROM    doctor AS dct
        LEFT JOIN Nosource AS nos ON dct.docNumber = nos.docNumber
GROUP BY dct.ID ,
        dct.NAME ,
        dct.docNumber ,
        nos.workTime
は確かに簡単です。小さなグループでも大丈夫です。まだ何を売っていますか?
今は変更が必要です。条件によってマッチングしなければなりません。要求番号のソーステーブルのworkTimeは現在の日付よりも大きくて有効です。そうでなければ一致しません。workTime条件が合わない医師は、対応するPersonumSounceCOUNTフィールドの値は0であるべきです。例えば、Jim医師は条件に合致する番号ソースがなく、そのPersonumSounceCOUNTフィールドの値は0であるべきです。空を40度見上げて、whereキーワードでフィルタして、一度に調べられますか?試してみましょう

SELECT  COUNT(nos.id) AS PersonNumSounceCOUNT , --
        dct.ID ,
        dct.NAME ,
        dct.docNumber ,
        nos.workTime
FROM    doctor AS dct
        LEFT JOIN Nosource AS nos ON dct.docNumber = nos.docNumber
WHERE   DATEDIFF(day, GETDATE(), nos.workTime) > 0
GROUP BY dct.ID ,
        dct.NAME ,
        dct.docNumber ,
        nos.workTime
は上のコードを書く人がいると信じています。しかし、検索を実行したら、全く要求に合わないですね。Jim先生の基本情報と表の記録も全部フィルタされました。なくなりました。どういうことですか?
理由は簡単ですよ。クエリに接続した後に「where」というキーワードを使うと、クエリに接続した結果の集中データがフィルタされます。右の表(号源表)の条件が合わないため、左の表(医師表)のデータがフィルタされます。
このような現象が発生します(Jim医師の情報と記録がなくなってしまいました)。一回で調べたいですが、可能ですか?一体どうやって実現すればいいですか?
正しい書き方は、

SELECT  COUNT(nos.id) AS PersonNumSounceCOUNT , --
        dct.ID ,
        dct.NAME ,
        dct.docNumber ,
        nos.workTime
FROM    doctor AS dct
        LEFT JOIN ( SELECT  *
                    FROM    Nosource
                    WHERE   DATEDIFF(day, GETDATE(), workTime) > 0
                  ) AS nos ON dct.docNumber = nos.docNumber
GROUP BY dct.ID ,
        dct.NAME ,
        dct.docNumber ,
        nos.workTime
でもう一度実行したら、やはりOKです。要求に満足した結果です。右のテーブルをフィルタリングするだけで、フィルタリングした結果セットをクエリーに接続する右のテーブルとして接続します。
簡潔で高性能なsql文を書くには、強い論理的思考能力(数学と切り離せない)と経験が必要です。もっと簡単な書き方もあります。

SELECT  sum(case when nos.workTime>getdate then 1 else 0 end) AS PersonNumSounceCOUNT , --
dct.ID AS docID ,
dct.NAME ,
dct.docNumber
FROM    doctor AS dct
LEFT JOIN Nosource AS nos ON dct.docNumber = nos.docNumber
GROUP BY dct.ID ,
dct.NAME ,
dct.docNumber
さんはこのように説明します。みんなが理解できるかどうかは分かりません。筆者の表現能力とレベルは確かに限られています。