最近一直在忙,也沒有太多時間停留下來寫博客。晚上遇到一個覺得很有趣的sql題,可能對初學者和我這種菜鳥會有幫助,所以決定分享給大家。
由於筆者天生笨拙,且思維不嚴謹,也實在不擅長寫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.查出每位醫生的相關信息,以及該醫生所擁有的號源數量。
這簡直太簡單了,可能連剛學會helloWorld和一點點數據庫基礎的朋友都會嚴重真心BS。不過代碼還是寫出來。
--簡單的分組查詢即可搞定 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條件不匹配的醫生,對應的PersonNumSounceCOUNT字段的值應為0 ;例如:Jim醫生沒有匹配和符合條件的號源,其PersonNumSounceCOUNT字段值應為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
這樣去解釋,不知道大家是否能夠理解,反正大致意思就是這樣的。筆者的表達能力和水平確實有限,難免有偏差,望讀者諒解!
本文出自http://www.cnblogs.com/dinglang/,轉載請注明出處。