跟著小郭郭一起學 SQL Server-21 Select(6)

在上次介紹子查詢之後,這次我們要介紹日期與時間為查詢條件時需要注意的事情。

那就讓我們直接示範為什麼要特別注意的原因吧。
我們一樣使用第19篇所使用的病人與診斷資料表。

SELECT TOP (1000) [Patient_ID]
      ,[Disease_diagnosed]
      ,[Diagnose_Date]
      ,[Is_Recovered]
  FROM [TEST001].[dbo].[Diagnosis]

OK,如果我們要用日期區間為條件來找出這筆 ID為 1 的資料,而我們第一次嘗試:

SELECT TOP (1000) [Patient_ID]
      ,[Disease_diagnosed]
      ,[Diagnose_Date]
      ,[Is_Recovered]
  FROM [TEST001].[dbo].[Diagnosis]

    where [Diagnose_Date] between  2024-07-05 and 2024-07-06


就會發現這樣查詢是找不出東西的,因為SQL Server 沒有正確地把這個 2024/07/05 識別成一個日期的值,要讓SQL server 正確的識別,必須加上單引號 ‘ 把日期框住。

另外一個狀況是這樣的:

SELECT TOP (1000) [Patient_ID]
      ,[Disease_diagnosed]
      ,[Diagnose_Date]
      ,[Is_Recovered]
  FROM [TEST001].[dbo].[Diagnosis]

    where [Diagnose_Date] =  '2024-07-05' 

為什麼會找不到資料呢?因為我們查詢的是 Datetime 欄位,在查詢 Datetime 欄位時,只有日期而沒有特別標明時間的話,會被識別為 2024-07-05 00:00:00 ,所以我們通常會這樣寫:

SELECT TOP (1000) [Patient_ID]
      ,[Disease_diagnosed]
      ,[Diagnose_Date]
      ,[Is_Recovered]
  FROM [TEST001].[dbo].[Diagnosis]

     WHERE [Diagnose_Date] >= '2024-07-05'
  AND [Diagnose_Date] < '2024-07-06';

另外值得注意一點:由於 between 是包含頭尾的關係。
使用這種寫法要特別留意頭尾該不該被包含進來:

SELECT TOP (1000) [Patient_ID]
      ,[Disease_diagnosed]
      ,[Diagnose_Date]
      ,[Is_Recovered]
  FROM [TEST001].[dbo].[Diagnosis]

     WHERE [Diagnose_Date] >= '2024-07-05'
  AND [Diagnose_Date] < '2024-07-06';

以上就是這次的內容,在下次我們會開始說明 Group by

跟著小郭郭一起學 SQL Server-20 Select(5)

在上次介紹完 Set Operators 之後,這次我們要來介紹子查詢的概念。

子查詢的意思就是,在我進行查詢的過程中,使用的查詢條件並不是特定的條件,而是再做一個查詢,以這個查詢的結果作為查詢條件,而這個用於設定查詢條件的查詢就是子查詢。

那讓我們回顧上一篇,我們透過了Set Operator 中 的 Intersect 找到了正在住院但已經痊癒的病人。

SELECT  [Patient_ID]

  FROM [TEST001].[dbo].[Patient]
  where [Hospitalized]='Y'

  Intersect

SELECT  [Patient_ID]

  FROM [TEST001].[dbo].[Diagnosis]
    where [Is_Recovered]='Y'

但我們這時只抓到的是病人的 ID,那如果我們要的是這些病人的基本資料呢?
這時我們就會用到常見的子查詢寫法,也就是使用之前介紹的 in():

SELECT * from [Patient]

WHERE [Patient_ID] IN 
(

SELECT  [Patient_ID]

  FROM [TEST001].[dbo].[Patient]
  where [Hospitalized]='Y'

  Intersect

SELECT  [Patient_ID]

  FROM [TEST001].[dbo].[Diagnosis]
    where [Is_Recovered]='Y'
)

像這樣,透過我們使用 set operator 所查詢的結果作為查詢條件去查詢 [Patient] 這張表。
我們就成功地把正在住院但已經痊癒的病人的完整資料叫出來了。
而除了在 WHERE 子句中進行子查詢之外,也可以在 FROM 子句中進行子查詢。
我們將會在之後介紹 Group by 與 Having 時一起介紹。

跟著小郭郭一起學 SQL Server-19 Select(4)

在上次我們介紹過可以指定查詢範圍的 = 與 in,以及可以合併多個條件的 AND 與 OR 之後。
我們要介紹的是這些概念的延伸,INTERSECT、UNION 與 EXCEPT。

這三個 set operators 分別對應的是 交集、聯集與差集。

而 INTERSECT、UNION 與 EXCEPT 通常用於一個情形:
當我必須對兩個 Query 結果進行比對,特別是這兩個 Query 來自於不同Table的時候。
為了示範這三個 set operators 的效果,我們就來建立兩張表。

Create Table Patient

(	
	Patient_ID INT,
	Patient_Name nvarchar(50),
	Patient_Gender nvarchar(2),
	Patient_Height smallint,
	Patient_Weight smallint,
	Hospitalized nvarchar(2)

);


INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (1, 'Linda Clark', 'M', 198, 103, 'N');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (2, 'Sarah Johnson', 'F', 143, 62, 'Y');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (3, 'Michael Clark', 'M', 166, 72, 'Y');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (4, 'John Brown', 'F', 192, 112, 'N');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (5, 'David Smith', 'M', 195, 110, 'N');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (6, 'David Clark', 'F', 157, 97, 'N');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (7, 'Robert Lee', 'F', 181, 64, 'Y');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (8, 'Jessica Johnson', 'F', 169, 74, 'Y');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (9, 'Jessica Lewis', 'F', 165, 109, 'N');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (10, 'Emily Brown', 'M', 175, 65, 'Y');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (11, 'Emily Smith', 'F', 177, 93, 'Y');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (12, 'Michael Brown', 'F', 149, 116, 'Y');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (13, 'Michael Martinez', 'M', 198, 59, 'N');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (14, 'Emily Clark', 'M', 154, 87, 'N');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (15, 'Jessica Johnson', 'F', 177, 42, 'N');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (16, 'David Anderson', 'M', 144, 81, 'N');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (17, 'Jessica Johnson', 'M', 148, 83, 'Y');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (18, 'James Taylor', 'M', 168, 67, 'N');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (19, 'David Clark', 'F', 165, 73, 'N');
INSERT INTO Patient (Patient_ID, Patient_Name, Patient_Gender, Patient_Height, Patient_Weight, Hospitalized) VALUES (20, 'Sarah Lewis', 'F', 172, 64, 'Y');
Create Table Diagnosis

(	
	Patient_ID INT,
	Disease_diagnosed nvarchar(50),
	Diagnose_Date datetime,
	Is_Recovered nvarchar(2)

);

INSERT INTO Diagnosis (Patient_ID, Disease_diagnosed, Diagnose_Date, Is_Recovered) VALUES
    (1, 'Influenza', '2024-07-05 10:30:00', 'Y'),
    (2, 'Pneumonia', '2024-06-20 15:45:00', 'Y'),
    (3, 'Diabetes', '2024-08-12 09:15:00', 'N'),
    (4, 'Hypertension', '2024-07-28 11:00:00', 'Y'),
    (5, 'Bronchitis', '2024-09-01 14:20:00', 'N'),
    (6, 'Asthma', '2024-11-15 16:30:00', 'Y'),
    (7, 'Allergies', '2024-02-28 08:45:00', 'N'),
    (8, 'Migraine', '2024-05-03 13:15:00', 'Y'),
    (9, 'Arthritis', '2024-12-10 10:00:00', 'N'),
    (10, 'Gastroenteritis', '2024-04-18 17:30:00', 'Y'),
    (11, 'Influenza', '2024-01-12 11:45:00', 'Y'),
    (12, 'Pneumonia', '2024-09-25 09:00:00', 'N'),
    (13, 'Diabetes', '2024-06-08 15:15:00', 'Y'),
    (14, 'Hypertension', '2024-03-20 12:30:00', 'N'),
    (15, 'Bronchitis', '2024-10-05 18:00:00', 'Y'),
    (16, 'Asthma', '2024-08-22 14:15:00', 'N'),
    (17, 'Allergies', '2024-12-21 10:45:00', 'Y'),
    (18, 'Migraine', '2024-07-04 16:30:00', 'N'),
    (19, 'Arthritis', '2024-02-15 13:00:00', 'Y'),
    (20, 'Gastroenteritis', '2024-11-01 11:15:00', 'N');

在建立好之後,我們可以開始輸入一些簡單的條件做查詢,像是找出已經痊癒的病人跟正在住院的病人:

SELECT TOP (1000) [Patient_ID]
      ,[Patient_Name]
      ,[Patient_Gender]
      ,[Patient_Height]
      ,[Patient_Weight]
      ,[Hospitalized]
  FROM [TEST001].[dbo].[Patient]
  where [Hospitalized]='Y'

SELECT TOP (1000) [Patient_ID]
      ,[Disease_diagnosed]
      ,[Diagnose_Date]
      ,[Is_Recovered]
  FROM [TEST001].[dbo].[Diagnosis]
    where [Is_Recovered]='Y'

而當我們要開始找正在住院但已經痊癒的病人的時候,我們就要用到第一個 set operator:
交集 Intersect。
而當我們使用 set operator 時,要注意的是,兩段 Query 的欄位數必須是一樣的。

SELECT  [Patient_ID]

  FROM [TEST001].[dbo].[Patient]
  where [Hospitalized]='Y'

  Intersect

SELECT  [Patient_ID]

  FROM [TEST001].[dbo].[Diagnosis]
    where [Is_Recovered]='Y'

像這樣,我們就找出了已經痊癒又住院中的 Patient_ID

接下來我們介紹第二個 set operator:Union,Union 是聯集,也就是 或 的概念。

SELECT  [Patient_ID]

  FROM [TEST001].[dbo].[Patient]
  where [Hospitalized]='Y'

  UNION

SELECT  [Patient_ID]

  FROM [TEST001].[dbo].[Diagnosis]
    where [Is_Recovered]='Y'

將剛才放在兩段Query之間的 Intersect 換成 Union,我們就可以得到
在住院的病患和痊癒的病患的聯集,也就是這個病患在住院或是已經痊癒。
不過要留意一件事情:在我們最一開始的兩段 Query 中,查詢的結果是有 20 筆的
但這裡的 Union 的結果只有 15 筆,這是因為 這三個 Set operator 都會預設去掉重複值。
而在這些 Set operator 中,只有 Union 可以透過加上 All 來避免去除重複。

最後一個 Set operator 則是 except,
當我們將剛才放在兩段Query之間的 Union換成 except,
我們得到的會是住院但尚未痊癒的 Patient ID:

SELECT  [Patient_ID]

  FROM [TEST001].[dbo].[Patient]
  where [Hospitalized]='Y'

  except

SELECT  [Patient_ID]

  FROM [TEST001].[dbo].[Diagnosis]
    where [Is_Recovered]='Y'

而要注意的是,Union 跟 Intersect 的上下兩段Query如果互換是不影響查詢結果的,
但 except 則會影響。

以上就是 INTERSECT、UNION 與 EXCEPT 這些 Set Operator 的概念。
下一次我們將介紹 子查詢 的概念。

跟著小郭郭一起學 SQL Server-16 Select(1)

當你覺得因為挫敗自己很糟時,想想如果是你最親愛的人如此,
你會不會覺得他就因此不值得你的喜愛了?
如果不會,那代表你應該也還是值得被喜愛的。

在上次我們介紹了如何對資料欄位建立約束,搭配之前的資料表建立與修改,我們其實已經對如何定義自己想要儲存的資料有個基本的認識,接下來我們將開始介紹如何在這些資料表中查詢、新增、修改、刪除資料,讓我們開始吧。

首先是 Select (查詢),他的結構如下:

Select 欄位名稱
From 資料表名稱
Where 查詢條件
group by 彙整單位(當需要將查詢結果以特定欄位的資料為單位彙整時使用)
Having 彙整查詢條件(當需要將查詢結果以特定欄位的資料為單位彙整時使用)
order by 排序條件

這個結構的順序無法互換,有一個比較簡單的記法:
當你看著 qwerty 型的英文鍵盤時,就會發現鍵盤上從左到右分別是
S W F G H O
只要將 F 跟 W 的順序對調就是整個結構的順序。
我們先從最基本的開始,不過在這之前,我們得先建立資料表。

請打開 New Query 後,複製下列文字並按下 F5 執行:

Create Table Test_OF_Select
(
	Student_ID int Not Null ,
	Course_ID  nvarchar(50) Not Null,
	Score CHAR(5)
	
);

INSERT INTO Test_OF_Select (Student_ID,Course_ID,Score)Values
('1001','0020','A'),
('1002','0020','B'),
('1003','0020','C'),
('1004','0020','C'),
('1005','0020','C'),
('1006','0020','C'),
('1007','0020','C'),
('1008','0020','C'),
('1009','0020','C'),
('1010','0020','C')

建立完成之後,對著已建立好的資料表點選右鍵:Select Top 1000 rows

就可以看到一個最基本的 Select

SELECT TOP (1000) [Student_ID]
      ,[Course_ID]
      ,[Score]
  FROM [TEST001].[dbo].[Test_OF_Select]

這段語法的中文是這樣說的:
從 [TEST001].[dbo].[Test_OF_Select] 這張表中選取 前1000筆 的資料中的 [Student_ID] ,[Course_ID] ,[Score] 欄位。

這就是今天的內容,下次我們將介紹的是 Where 條件。

跟著小郭郭一起學 SQL Server-15 Foreign Key(1)

三折肱而成良醫

在上回我們介紹了 Primary Key 約束是什麼,以及如何建立、檢視與刪除 Primary Key,
接下來我們將介紹什麼是 Foreign Key。


Foreign Key 約束會將能進入一個欄位的資料局限於是一個會另一張表的特定欄位裡所有的資料的約束,當我們輸入資料時, SQL Server 就會參照另一個欄位,確認那個欄位有這筆資料才會成功,沒有這筆資料時,該筆資料就沒辦法輸入。而我們稱被參照的來源資料表為 Parent,實際套用約束的資料表則是稱為 Child
所以當我們要建立與測試 Foreign Key 約束前,我們要先建立這個約束要參照資料表與欄位,這個欄位必須是該資料表的 Primary Key,並且這兩個資料型態必須相同。
請打開 New Query 後,複製下列文字並按下 F5 執行:

Create Table Foreign_Key_test_Parent
(
	Student_ID int Not Null Primary Key,
	Student_Name nvarchar(50),
	Date_of_Birth date
)

INSERT INTO Foreign_Key_test_Parent (Student_ID,Student_Name,Date_of_Birth)Values

('1001','Jeff','2022/02/13'),
('1002','Lily','2022/02/14'),
('1003','Tochter','2022/02/15')
SELECT TOP (10) * From Foreign_Key_test_Parent

這樣我們就可以確認資料表已經建立,並且裡面的 Student_ID 有 1001~1003了。

接下來,我們來建立實際使用 Foreign Key 約束的資料表,並在建立資料表時對欄位加入Foreign Key 約束,加入的格式如下:
欄位名稱 資料類型 是否允許 NUll

Constraint 約束名稱 Foreign Key References 資料表(欄位)

請打開 New Query 後,複製下列文字並按下 F5 執行:

Create Table Foreign_Key_test_Child
(
	Student_ID int Not Null 
	Constraint Foreign_Key_Student_ID Foreign Key References Foreign_Key_test_Parent(Student_ID),
	Course_ID  nvarchar(50) Not Null,
	Score CHAR(5)
	
);

INSERT INTO Foreign_Key_test_Child (Student_ID,Course_ID,Score)Values
('1001','0020','A'),
('1002','0020','B'),
('1003','0020','C'),

SELECT TOP (10) * From Foreign_Key_test_Child 

這樣就可以看到 1001~1003 的資料都有被成功寫入。接下來我們輸入一筆不存在於 Parent 資料表的欄位的資料。

請打開 New Query 後,複製下列文字並按下 F5 執行:

INSERT INTO Foreign_Key_test_Child (Student_ID,Course_ID,Score)Values

('1004','0020','D')

就會看到被擋下來的錯誤訊息。

接下來則是如何檢視已經被建立的 Foreign Key,一種方式是直接到資料表的 Keys 中檢視。


另一種則是透過 information schema 查詢,請打開 New Query 後,複製下列文字並按下 F5 執行:

Select * from INFORMATION_SCHEMA.KEY_COLUMN_USAGE

刪除 Foreign Key 則是使用 Alter table 資料表名稱 Drop constraint Foreign Key 名稱,

請打開 New Query 後,複製下列文字並按下 F5 執行:

Alter table Foreign_Key_test_Child
Drop constraint Foreign_Key_Student_ID

INSERT INTO Foreign_Key_test_Child (Student_ID,Course_ID,Score)Values

('1004','0020','D')

SELECT TOP (10) * From Foreign_Key_test_Child 

就可以看到約束消失,剛才因為約束無法被 insert 的資料可以成功insert 了。

而同樣使用 Alter Table 指令則可以再把 Foreign Key 加回來,
請打開 New Query 後,複製下列文字並按下 F5 執行:

Delete Foreign_Key_test_Child 

Alter table Foreign_Key_test_Child
ADD constraint Foreign_Key_Student_ID
Foreign Key(Student_ID)  References Foreign_Key_test_Parent(Student_ID);
INSERT INTO Foreign_Key_test_Child (Student_ID,Course_ID,Score)Values

('1004','0020','D')

OK,這就是今天的內容,接下來我們將會開始講解如何在資料表中查詢/新增/修改/刪除資料。

跟著小郭郭一起學 SQL Server-13 Primary Key (1)

定義問題的本質,界定影響的範圍,追尋造成的原因,規劃解決的方法。

在上次,我們介紹了 Null 以及 Check 兩種對資料內容的約束,在這次我們要介紹的是 Primary Key (PK) 約束的性質。

Primary Key 通常由一到數個欄位組成,這些欄位內的資料不能重複,一個資料表只能有一個 Primary Key,這樣的資料約束可以確保資料表內應該代表同一筆紀錄的資料不會被重複寫入。

舉例來說,如果 Application 在寫入資料到資料表的過程中因為各式各樣的原因傳送到一半失敗了,在重新寫入的時候就可以透過 Primary Key 約束的設定避免同樣的資料,像是訂單,被重新寫入兩次。

而在建立 Primary Key 的同時也會對資料表內的資料建立索引,如果在做對資料的查詢時,有使用到 Primary Key 的欄位,就可以加快查詢速度,因此理想的狀況是資料庫裡的每一張資料表都有自己對應的 Primary Key 約束。

在下次,我們將介紹如何建立、檢視、修改、以及刪除 Primary key 約束。

跟著小郭郭一起學 SQL Server-12 資料預設值與資料檢查(2)

歲月你別催,該來的我不推,該還的還,該給的我給

———————————————————————————-

上次我們介紹的內容是在建立資料表的當下連帶建立欄位的預設值與對欄位資料的條件檢查,
這次我們要介紹的是為現有的資料表加上這些條件限制。

而要為現有的資料表加上欄位的資料限制,則需要用到以下的語法:
ALTER TABLE [資料表名稱]
ADD CONSTRAINT 限制的名稱 限制的類型 限制的內容

請開啟一個 new query,並執行以下的語法:

-------------新增一個資料表
CREATE TABLE [dbo].[table_data_constraint_test_3](
	[student_no] [nvarchar](10) NOT NULL,
	[student_name] [nvarchar](50) NULL,
	[date_of_birth] [date] NULL
) ON [PRIMARY]
GO
-------------新增一個資料表
------新增與前一篇相同的限制
ALTER TABLE [table_data_constraint_test_3] 

ADD 
	CONSTRAINT age_above_18_2 CHECK ([date_of_birth]<'2003/08/25'),


	CONSTRAINT  default_as_test_2  DEFAULT  'test' For student_name

------新增與前一篇相同的限制


在執行成功之後,就可以在資料表[table_data_constraint_test_3]下的Constraints 中看到新增的限制名稱。

但對已經建立的資料表設立限制之前,最好先檢查一下現在的資料表內有沒有不符合這些限制的資料,不然就有可能失敗。

請開啟一個 new query,並執行以下的語法:

------建立新的資料表
CREATE TABLE [dbo].[table_data_constraint_test_4](
	[student_no] [nvarchar](10) NOT NULL,
	[student_name] [nvarchar](50) NULL,
	[date_of_birth] [date] NULL
) ON [PRIMARY]
GO
------建立新的資料表
------插入不符合限制規範的資料

INSERT INTO [table_data_constraint_test_4]
	([student_no],[student_name],[date_of_birth])
	values('002','SIX','2005/01/01')
------插入不符合限制規範的資料
------新增與前一篇相同的限制
ALTER TABLE [table_data_constraint_test_4] 

ADD 
	CONSTRAINT age_above_18_3 CHECK ([date_of_birth]<'2003/08/25'),

	CONSTRAINT  default_as_test_3  DEFAULT  'test' For student_name
 ------新增與前一篇相同的限制

——新增與前一篇相同的限制

就會看到錯誤訊息,顯示目前資料表裡已有的資料與欲建立的內容產生衝突:

而如果要解除這些已經建立的限制,則需要用到以下的語法:

ALTER TABLE [資料表名稱]
DROP CONSTRAINT 限制的名稱

請開啟一個 new query,並執行以下的語法:

------刪除既有的限制
ALTER TABLE [table_data_constraint_test_3] 

DROP 
	CONSTRAINT age_above_18_2,
	CONSTRAINT  default_as_test_2
------刪除既有的限制

這樣就可以刪除目前已經建立的限制。

可以與第一張圖比較,就會發現 [table_data_constraint_test_3] 底下的 Constraints 中,原有的限制已經消失。

這就是這次的內容,在下一次我們會講解 Primary 與 Foreign Key 資料限制。