forked from anthonydb/practical-sql-2
-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathChapter_13.sql
More file actions
354 lines (289 loc) · 9.82 KB
/
Chapter_13.sql
File metadata and controls
354 lines (289 loc) · 9.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
---------------------------------------------------------------------------
-- 실용 SQL
-- by Anthony DeBarros
----------------------------------------------------------------------------
-- 코드 13-1: WHERE 절에서 하위 쿼리 사용하기
SELECT county_name,
state_name,
pop_est_2019
FROM us_counties_pop_est_2019
WHERE pop_est_2019 >= (
SELECT percentile_cont(.9) WITHIN GROUP (ORDER BY pop_est_2019)
FROM us_counties_pop_est_2019
)
ORDER BY pop_est_2019 DESC;
-- 코드 13-2: DELETE 내부 WHERE 절에서 하위 쿼리 사용하기
CREATE TABLE us_counties_2019_top10 AS
SELECT * FROM us_counties_pop_est_2019;
DELETE FROM us_counties_2019_top10
WHERE pop_est_2019 < (
SELECT percentile_cont(.9) WITHIN GROUP (ORDER BY pop_est_2019)
FROM us_counties_2019_top10
);
SELECT count(*) FROM us_counties_2019_top10;
-- 코드 13-3: FROM 절에서 파생된 테이블로 하위 쿼리
SELECT round(calcs.average, 0) as average,
calcs.median,
round(calcs.average - calcs.median, 0) AS median_average_diff
FROM (
SELECT avg(pop_est_2019) AS average,
percentile_cont(.5)
WITHIN GROUP (ORDER BY pop_est_2019)::numeric AS median
FROM us_counties_pop_est_2019
)
AS calcs;
-- 코드 13-4: 파생 테이블 두 개 조인하기
SELECT census.state_name AS st,
census.pop_est_2018,
est.establishment_count,
round((est.establishment_count/census.pop_est_2018::numeric) * 1000, 1)
AS estabs_per_thousand
FROM
(
SELECT st,
sum(establishments) AS establishment_count
FROM cbp_naics_72_establishments
GROUP BY st
)
AS est
JOIN
(
SELECT state_name,
sum(pop_est_2018) AS pop_est_2018
FROM us_counties_pop_est_2019
GROUP BY state_name
)
AS census
ON est.st = census.state_name
ORDER BY estabs_per_thousand DESC;
-- 코드 13-5: 열 목록에 하위 쿼리 추가하기
SELECT county_name,
state_name AS st,
pop_est_2019,
(SELECT percentile_cont(.5) WITHIN GROUP (ORDER BY pop_est_2019)
FROM us_counties_pop_est_2019) AS us_median
FROM us_counties_pop_est_2019;
-- 코드 13-6: 계산에 하위 쿼리 사용하기
SELECT county_name,
state_name AS st,
pop_est_2019,
pop_est_2019 - (SELECT percentile_cont(.5) WITHIN GROUP (ORDER BY pop_est_2019)
FROM us_counties_pop_est_2019) AS diff_from_median
FROM us_counties_pop_est_2019
WHERE (pop_est_2019 - (SELECT percentile_cont(.5) WITHIN GROUP (ORDER BY pop_est_2019)
FROM us_counties_pop_est_2019))
BETWEEN -1000 AND 1000;
-- 코드 13-7: retirees 테이블 만들고 채우기
CREATE TABLE retirees (
id int,
first_name text,
last_name text
);
INSERT INTO retirees
VALUES (2, 'Janet', 'King'),
(4, 'Michael', 'Taylor');
-- 코드 13-8: IN 연산자에 대한 값 생성하기
SELECT first_name, last_name
FROM employees
WHERE emp_id IN (
SELECT id
FROM retirees)
ORDER BY emp_id;
-- 코드 13-9: WHERE EXISTS를 사용한 상관 하위 쿼리
SELECT first_name, last_name
FROM employees
WHERE EXISTS (
SELECT id
FROM retirees
WHERE id = employees.emp_id);
-- 코드 13-10: WHERE NOT EXISTS를 사용한 상관 하위 쿼리
SELECT first_name, last_name
FROM employees
WHERE NOT EXISTS (
SELECT id
FROM retirees
WHERE id = employees.emp_id);
-- 코드 13-11: LATERAL 하위 쿼리를 사용한 FROM 절
SELECT county_name,
state_name,
pop_est_2018,
pop_est_2019,
raw_chg,
round(pct_chg * 100, 2) AS pct_chg
FROM us_counties_pop_est_2019,
LATERAL (SELECT pop_est_2019 - pop_est_2018 AS raw_chg) rc,
LATERAL (SELECT raw_chg / pop_est_2018::numeric AS pct_chg) pc
ORDER BY pct_chg DESC;
-- 코드 13-12: LATERAL 하위 쿼리를 사용한 JOIN
ALTER TABLE teachers ADD CONSTRAINT id_key PRIMARY KEY (id);
CREATE TABLE teachers_lab_access (
access_id bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
access_time timestamp with time zone,
lab_name text,
teacher_id bigint REFERENCES teachers (id)
);
INSERT INTO teachers_lab_access (access_time, lab_name, teacher_id)
VALUES ('2022-11-30 08:59:00-05', 'Science A', 2),
('2022-12-01 08:58:00-05', 'Chemistry B', 2),
('2022-12-21 09:01:00-05', 'Chemistry A', 2),
('2022-12-02 11:01:00-05', 'Science B', 6),
('2022-12-07 10:02:00-05', 'Science A', 6),
('2022-12-17 16:00:00-05', 'Science B', 6);
SELECT t.first_name, t.last_name, a.access_time, a.lab_name
FROM teachers t
LEFT JOIN LATERAL (SELECT *
FROM teachers_lab_access
WHERE teacher_id = t.id
ORDER BY access_time DESC
LIMIT 2) a
ON true
ORDER BY t.id;
-- 공통 테이블 표현식
-- 코드 13-13: 인구가 100,000명 이상인 카운티 수 구하기
WITH large_counties (county_name, state_name, pop_est_2019)
AS (
SELECT county_name, state_name, pop_est_2019
FROM us_counties_pop_est_2019
WHERE pop_est_2019 >= 100000
)
SELECT state_name, count(*)
FROM large_counties
GROUP BY state_name
ORDER BY count(*) DESC;
-- 보너스: 같은 쿼리를 다음과 같이 적을 수 있습니다.
SELECT state_name, count(*)
FROM us_counties_pop_est_2019
WHERE pop_est_2019 >= 100000
GROUP BY state_name
ORDER BY count(*) DESC;
-- 코드 13-14: CTE를 사용한 테이블 조인
WITH
counties (st, pop_est_2018) AS
(SELECT state_name, sum(pop_est_2018)
FROM us_counties_pop_est_2019
GROUP BY state_name),
establishments (st, establishment_count) AS
(SELECT st, sum(establishments) AS establishment_count
FROM cbp_naics_72_establishments
GROUP BY st)
SELECT counties.st,
pop_est_2018,
establishment_count,
round((establishments.establishment_count /
counties.pop_est_2018::numeric(10,1)) * 1000, 1)
AS estabs_per_thousand
FROM counties JOIN establishments
ON counties.st = establishments.st
ORDER BY estabs_per_thousand DESC;
-- 코드 13-15: CTE로 코드 반복 줄이기
WITH us_median AS
(SELECT percentile_cont(.5)
WITHIN GROUP (ORDER BY pop_est_2019) AS us_median_pop
FROM us_counties_pop_est_2019)
SELECT county_name,
state_name AS st,
pop_est_2019,
us_median_pop,
pop_est_2019 - us_median_pop AS diff_from_median
FROM us_counties_pop_est_2019 CROSS JOIN us_median
WHERE (pop_est_2019 - us_median_pop)
BETWEEN -1000 AND 1000;
-- 교차 표
-- tablefunc 모듈을 통한 crosstab() 함수 설치
CREATE EXTENSION tablefunc;
-- 코드 13-16: ice_cream_survey 테이블 생성 및 채우기
CREATE TABLE ice_cream_survey (
response_id integer PRIMARY KEY,
office text,
flavor text
);
COPY ice_cream_survey
FROM 'C:\YourDirectory\ice_cream_survey.csv'
WITH (FORMAT CSV, HEADER);
-- 데이터 보기
SELECT *
FROM ice_cream_survey
ORDER BY response_id
LIMIT 5;
-- 코드 13-17: 아이스크림 설문조사 크로스 탭 생성하기
SELECT *
FROM crosstab('SELECT office,
flavor,
count(*)
FROM ice_cream_survey
GROUP BY office, flavor
ORDER BY office',
'SELECT flavor
FROM ice_cream_survey
GROUP BY flavor
ORDER BY flavor')
AS (office text,
chocolate bigint,
strawberry bigint,
vanilla bigint);
-- 코드 13-18: temperature_readings 테이블 만들고 채우기
CREATE TABLE temperature_readings (
station_name text,
observation_date date,
max_temp integer,
min_temp integer,
CONSTRAINT temp_key PRIMARY KEY (station_name, observation_date)
);
COPY temperature_readings
FROM 'C:\YourDirectory\temperature_readings.csv'
WITH (FORMAT CSV, HEADER);
-- 코드 13-19: 온도 판독 값 크로스 탭 생성하기
SELECT *
FROM crosstab('SELECT
station_name,
date_part(''month'', observation_date),
percentile_cont(.5)
WITHIN GROUP (ORDER BY max_temp)
FROM temperature_readings
GROUP BY station_name,
date_part(''month'', observation_date)
ORDER BY station_name',
'SELECT month
FROM generate_series(1,12) month')
AS (station text,
jan numeric(3,0),
feb numeric(3,0),
mar numeric(3,0),
apr numeric(3,0),
may numeric(3,0),
jun numeric(3,0),
jul numeric(3,0),
aug numeric(3,0),
sep numeric(3,0),
oct numeric(3,0),
nov numeric(3,0),
dec numeric(3,0)
);
-- 코드 13-20: CASE로 온도 데이터 재분류하기
SELECT max_temp,
CASE WHEN max_temp >= 90 THEN 'Hot'
WHEN max_temp >= 70 AND max_temp < 90 THEN 'Warm'
WHEN max_temp >= 50 AND max_temp < 70 THEN 'Pleasant'
WHEN max_temp >= 33 AND max_temp < 50 THEN 'Cold'
WHEN max_temp >= 20 AND max_temp < 33 THEN 'Frigid'
WHEN max_temp < 20 THEN 'Inhumane'
ELSE 'No reading'
END AS temperature_group
FROM temperature_readings
ORDER BY station_name, observation_date;
-- 코드 13-21: CTE에서 CASE 사용하기
WITH temps_collapsed (station_name, max_temperature_group) AS
(SELECT station_name,
CASE WHEN max_temp >= 90 THEN 'Hot'
WHEN max_temp >= 70 AND max_temp < 90 THEN 'Warm'
WHEN max_temp >= 50 AND max_temp < 70 THEN 'Pleasant'
WHEN max_temp >= 33 AND max_temp < 50 THEN 'Cold'
WHEN max_temp >= 20 AND max_temp < 33 THEN 'Frigid'
WHEN max_temp < 20 THEN 'Inhumane'
ELSE 'No reading'
END
FROM temperature_readings)
SELECT station_name, max_temperature_group, count(*)
FROM temps_collapsed
GROUP BY station_name, max_temperature_group
ORDER BY station_name, count(*) DESC;