├── DDL.sql ├── DML.sql ├── DML └── DML.1.sql ├── README.md └── SELECT(learning stage) ├── 14-46 SELECT learning stage.md ├── 47-61 SELECT learning stage.md ├── 62-76 SELECT learning stage.md └── 77-xx SELECT learning stage.md /DDL.sql: -------------------------------------------------------------------------------- 1 | -- Ships db 2 | create table Battles ( 3 | name char(20) primary key, 4 | date Date not null 5 | ); 6 | 7 | create table Outcomes ( 8 | ship char(50) 9 | , battle char(20) References Battles(name) 10 | , result char(10) not null 11 | , primary key (ship, battle) 12 | ); 13 | 14 | create table Ships ( 15 | name varchar(50) not null 16 | , class varchar(50) not null 17 | , launched integer default null 18 | , primary key (name, class) 19 | ); -------------------------------------------------------------------------------- /DML.sql: -------------------------------------------------------------------------------- 1 | -- DML 19 task tests 2 | insert into Battles values 3 | ('B1', 41) -- B1 4 | ,('B2', 42) -- B2 5 | ,('B3', 43) -- B3 6 | ,('B4', 44) -- B4 7 | ,('B5', 45) -- B5 8 | ; 9 | 10 | insert into Outcomes (ship, battle, result) 11 | values 12 | ('A', 'B1','ok') 13 | ,('A', 'B2','damaged') 14 | ,('B', 'B3','damaged') 15 | ,('B', 'B4','sunk') 16 | ,('C', 'B3','damaged') 17 | ,('D', 'B5','damaged') 18 | ,('E', 'B4','ok') 19 | ; 20 | -------------------------------------------------------------------------------- /DML/DML.1.sql: -------------------------------------------------------------------------------- 1 | -- 1 2 | /* Добавить в таблицу PC следующую модель: 3 | code: 20 4 | model: 2111 5 | speed: 950 6 | ram: 512 7 | hd: 60 8 | cd: 52x 9 | price: 1100 */ 10 | sql 11 | insert into pc 12 | values(20, '2111', 950, 512, 60, '52x', 1100); 13 | 14 | -- 2 15 | /* Добавить в таблицу Product следующие продукты производителя Z: 16 | принтер модели 4003, ПК модели 4001 и блокнот модели 4002 */ 17 | insert into product values 18 | ('Z', '4003', 'Printer'), 19 | ('Z', '4001', 'PC'), 20 | ('Z', '4002', 'Laptop') 21 | 22 | -- 3 23 | /* Добавить в таблицу PC модель 4444 с кодом 22, 24 | имеющую скорость процессора 1200 и цену 1350. 25 | Отсутствующие характеристики должны быть восполнены 26 | значениями по умолчанию, принятыми для соответствующих столбцов. */ 27 | insert into pc (model, code, speed, price) 28 | values ('4444', 22, 1200, 1350) 29 | 30 | -- 4 31 | /* Для каждой группы блокнотов с одинаковым номером модели 32 | добавить запись в таблицу PC со следующими характеристиками: 33 | код: минимальный код блокнота в группе +20; 34 | модель: номер модели блокнота +1000; 35 | скорость: максимальная скорость блокнота в группе; 36 | ram: максимальный объем ram блокнота в группе *2; 37 | hd: максимальный объем hd блокнота в группе *2; 38 | cd: значение по умолчанию; 39 | цена: максимальная цена блокнота в группе, уменьшенная в 1,5 раза. 40 | Замечание. Считать номер модели числом. */ 41 | insert into PC 42 | (code, model, speed, ram, hd, price) 43 | select 44 | min(code)+20 code 45 | , cast(model as int)+1000 as model 46 | , max(speed) speed 47 | , max(ram)*2 ram 48 | , max(hd)*2 hd 49 | --, NULL as cd 50 | , max(price)/1.5 price 51 | from laptop 52 | group by model 53 | ; 54 | 55 | -- 5 56 | /* Удалить из таблицы PC компьютеры, 57 | имеющие минимальный объем диска или памяти. */ 58 | delete from pc 59 | where hd = (select min(hd) from pc) 60 | OR ram = (select min(ram) from pc) 61 | ; 62 | 63 | -- 6 64 | /* Удалить все блокноты, выпускаемые производителями, 65 | которые не выпускают принтеры. */ 66 | delete from laptop 67 | where model in ( 68 | -- all models whose makers don't produce printers 69 | select model from product 70 | where maker not in (select maker from product where type='Printer') 71 | ); 72 | 73 | -- 7 74 | /* Производство принтеров производитель A 75 | передал производителю Z. 76 | Выполнить соответствующее изменение */ 77 | update product 78 | set maker='Z' where (maker='A' and type='Printer'); 79 | 80 | -- 8 81 | /* Удалите из таблицы Ships все корабли, 82 | потопленные в сражениях. */ 83 | delete from Ships 84 | where name in ( 85 | select ship from outcomes 86 | where result='sunk' 87 | ); 88 | 89 | -- 9 90 | /* Измените данные в таблице Classes так, 91 | чтобы калибры орудий измерялись в сантиметрах (1 дюйм=2,5см), 92 | а водоизмещение в метрических тоннах (1 метрическая тонна = 1,1 тонны). 93 | Водоизмещение вычислить с точностью доцелых. */ 94 | update classes 95 | set bore=(bore*2.5) 96 | , displacement=ROUND(displacement*1.0/1.1, 0) 97 | ; 98 | 99 | -- 10 100 | /* Добавить в таблицу PC те модели ПК из Product, 101 | которые отсутствуют в таблице PC. 102 | 103 | При этом модели должны иметь следующие характеристики: 104 | 105 | 1. Код равен номеру модели плюс максимальный код, 106 | который был до вставки. 107 | 108 | 2. Скорость, объем памяти и диска, а также скорость CD 109 | должны иметь максимальные характеристики 110 | среди всех имеющихся в таблице PC. 111 | 112 | 3. Цена должна быть средней среди всех ПК, 113 | имевшихся в таблице PC до вставки.*/ 114 | insert into PC (code, model, speed, ram, hd, cd, price) 115 | select 116 | cast(model as int) + (select max(code) from pc) as code 117 | , model 118 | , (select max(speed) from pc ) as speed 119 | , (select max(ram) from pc) as ram 120 | , (select max(hd) from pc) as hd 121 | -- монстр-преобразователь типов 122 | , cast( 123 | (select max( 124 | -- делаем из скорости целое число 125 | -- чтобы правильно посчитать MAX 126 | CAST(substring(cd, 1, LEN(cd)-1) as INT) 127 | -- делаем обратно строковый тип 128 | -- чтобы добавить 'x' к значению 129 | ) from pc) as varchar 130 | ) + 'x' as cd 131 | , (select avg(price) from pc) as price 132 | from Product 133 | where 134 | type='PC' 135 | and model not in (select model from PC) 136 | ; 137 | 138 | -- 11 139 | /* Для каждой группы блокнотов с одинаковым номером модели 140 | добавить запись в таблицу PC со следующими характеристиками: 141 | код: минимальный код блокнота в группе +20; 142 | модель: номер модели блокнота +1000; 143 | скорость: максимальная скорость блокнота в группе; 144 | ram: максимальный объем ram блокнота в группе *2; 145 | hd: максимальный объем hd блокнота в группе *2; 146 | cd: cd c максимальной скоростью среди всех ПК; 147 | цена: максимальная цена блокнота в группе, уменьшенная в 1,5 раза */ 148 | insert into PC 149 | select 150 | min(code) + 20 as code 151 | , cast(model as int) + 1000 as model 152 | , max(speed) as speed 153 | , max(ram)*2 as ram 154 | , max(hd)*2 as hd 155 | , (select 156 | concat(MAX(CAST(replace(cd, 'x', '') as INT)), 'x') from pc 157 | ) as cd 158 | , max(price)/1.5 as price 159 | from Laptop 160 | group by model 161 | ; 162 | 163 | -- 12 164 | /* Добавьте один дюйм к размеру экрана каждого блокнота, 165 | выпущенного производителями E и B, и уменьшите его цену на $100. */ 166 | update Laptop 167 | set screen=screen+1, 168 | price=price-100 169 | where model in ( 170 | select model from product 171 | where (maker='E' OR maker='B') 172 | and type='Laptop' 173 | ) 174 | ; 175 | 176 | -- 13 177 | /* Ввести в базу данных информацию о том, 178 | что корабль Rodney был потоплен в битве, произошедшей 25/10/1944, 179 | а корабль Nelson поврежден - 28/01/1945. 180 | Замечание: считать, что дата битвы уникальна в таблице Battles. */ 181 | insert into Outcomes 182 | select 183 | 'Rodney' as ship 184 | , (select distinct name from Battles 185 | where date='1944-10-25' 186 | ) as battle 187 | , 'sunk' as result 188 | union 189 | select 190 | 'Nelson' 191 | , (select distinct name from Battles 192 | where date='1945-01-28' 193 | ) 194 | , 'damaged' 195 | ; 196 | 197 | -- 14 198 | /* Удалите классы, имеющие в базе данных менее трех кораблей 199 | (учесть корабли из Outcomes). */ 200 | delete from Classes 201 | where class in ( 202 | select 203 | c.class 204 | from ( 205 | select name, class from Ships 206 | union 207 | select ship, ship from Outcomes 208 | ) as sh 209 | right join Classes c on c.class=sh.class 210 | group by c.class 211 | having count(sh.name)<3 212 | ); 213 | 214 | -- 15 215 | /* Из каждой группы ПК с одинаковым номером модели в таблице PC 216 | удалить все строки кроме строки 217 | с наибольшим для этой группы кодом (столбец code).*/ 218 | delete from pc 219 | where code not in ( 220 | select 221 | max(code) 222 | from PC 223 | group by model 224 | ) 225 | ; 226 | 227 | -- 16 228 | /* Удалить из таблицы Product те модели, 229 | которые отсутствуют в других таблицах. */ 230 | delete from Product 231 | where model not in ( 232 | select model from pc 233 | union 234 | select model from laptop 235 | union 236 | select model from printer 237 | ) 238 | ; 239 | 240 | -- 17 241 | /* Удалить из таблицы PC компьютеры, 242 | у которых величина hd попадает в тройку наименьших значений. */ 243 | delete from pc 244 | where hd in ( 245 | select top 3 * from (select distinct hd from pc) as p 246 | order by hd asc 247 | ); 248 | 249 | -- 18 250 | /* Перенести все концевые пробелы, 251 | имеющиеся в названии каждого сражения в таблице Battles, 252 | в начало названия. */ 253 | update Battles 254 | set name=replace(name, rtrim(name), '')+rtrim(name); 255 | 256 | -- 19 257 | /* Потопить в следующем сражении суда, 258 | которые в первой своей битве были повреждены 259 | и больше не участвовали ни в каких сражениях. 260 | Если следующего сражения для такого судна не существует в базе данных, 261 | не вносить его в таблицу Outcomes. 262 | Замечание: в базе данных нет двух сражений, 263 | которые состоялись бы в один день. */ 264 | with exb as ( 265 | -- getting battles and NEXT battles 266 | -- in one table 267 | select 268 | name 269 | , lead(name) over(order by date asc) as nextbattle 270 | , date 271 | from Battles 272 | ), 273 | ob as ( 274 | -- Outcomes-Battles table 275 | select 276 | ship 277 | , battle 278 | , nextbattle 279 | , count(*) over(partition by ship) as numbattles 280 | , date 281 | , result 282 | from Outcomes o JOIN exb on o.battle=exb.name 283 | ) 284 | insert into Outcomes 285 | select 286 | ship, nextbattle as battle, 'sunk' as result 287 | from ob 288 | where numbattles=1 289 | and result='damaged' and nextbattle is not null 290 | ; 291 | 292 | -- 20 293 | /* Заменить любое количество повторяющихся пробелов 294 | в названиях кораблей из таблицы Ships на один пробел. */ 295 | -- Идея нагло взята из интернетов 296 | -- 1. Из строки типа 'AA BB' сделать 'AA<><><>BB' 297 | -- 2. Из строки 'AA<><><>BB' сделать 'AA<>BB' 298 | -- 3. <> заменить на пробел 299 | update Ships 300 | set name = 301 | replace( 302 | replace( 303 | replace(name, ' ', CHAR(17)+CHAR(18)) 304 | , CHAR(18)+CHAR(17), '' 305 | ) 306 | , CHAR(17)+CHAR(18), ' ' 307 | ) 308 | where name LIKE '% %'; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SQL-EX.RU 2 | 3 | Практическое владение языком SQL 4 | 5 | ## Описание 6 | 7 | Репозиторий содержит решения к некоторым упражнениям с сайта http://sql-ex.ru/ . На сайте требуется регистрация. 8 | 9 | - [`SELECT` (обучающий этап)](http://sql-ex.ru/learn_exercises.php), с [выбором СУБД](http://sql-ex.ru/exercises/index.php?act=learn) 10 | - [Data Manipulation Language (DML)](http://www.sql-ex.ru/dmlexercises.php) 11 | 12 | -------------------------------------------------------------------------------- /SELECT(learning stage)/14-46 SELECT learning stage.md: -------------------------------------------------------------------------------- 1 | ## 14 2 | 3 | Найти производителей, которые выпускают более одной модели, при этом все выпускаемые производителем модели являются продуктами одного типа. 4 | Вывести: maker, type 5 | 6 | ```sql 7 | Select maker, type 8 | from product 9 | where 10 | maker in ( 11 | -- производители, у которых все модели 12 | -- являются продуктами одного типа 13 | select maker from product 14 | group by maker 15 | having count(distinct type) = 1 16 | ) 17 | group by maker, type 18 | having 19 | count(model) > 1 20 | ``` 21 | 22 | | maker | type | 23 | |--------|---------| 24 | | D | Printer | 25 | 26 | ## 24 27 | 28 | Перечислите номера моделей любых типов, имеющих самую высокую цену по всей имеющейся в базе данных продукции. 29 | 30 | ```sql 31 | with mp as ( 32 | select model, price from pc 33 | union 34 | select model, price from printer 35 | union 36 | select model, price from laptop 37 | ) 38 | select model from mp where price = (select max(price) from mp) 39 | ``` 40 | 41 | | model | 42 | |-------| 43 | | 1750 | 44 | 45 | ## 25 46 | 47 | Найдите производителей принтеров, которые производят ПК с наименьшим объемом RAM и с самым быстрым процессором среди всех ПК, имеющих наименьший объем RAM. Вывести: Maker 48 | 49 | ```sql 50 | with M as ( 51 | select maker from product p 52 | where 53 | model in ( 54 | -- model with MIN ram AND 55 | -- MAX speed in MIN ram models 56 | select model from pc 57 | where 58 | ram=(select min(ram) from pc) 59 | and speed=( 60 | -- max speed from pc with minimum RAN 61 | select max(speed) from pc 62 | where ram=(select min(ram) from pc) 63 | ) 64 | ) 65 | ) 66 | select distinct maker from product 67 | where type='Printer' and maker in (select maker from M) 68 | ``` 69 | 70 | | maker | 71 | |-------| 72 | | A | 73 | | E | 74 | 75 | ## 26 76 | 77 | Найдите среднюю цену ПК и ПК-блокнотов, выпущенных производителем A (латинская буква). Вывести: одна общая средняя цена. 78 | 79 | ```sql 80 | with M as ( 81 | select model, price from pc 82 | union all 83 | select model, price from laptop 84 | ) 85 | select avg(M.price) "avg price" 86 | from product p join M on p.model=M.model 87 | where p.maker='A' 88 | ``` 89 | 90 | | avg price | 91 | |-----------| 92 | | 754.1666 | 93 | 94 | ## 28 95 | 96 | Используя таблицу `Product`, определить количество производителей, выпускающих по одной модели. 97 | 98 | ```sql 99 | select count(q.cm) "count of makers" from ( 100 | select count(model) cm from product 101 | group by maker 102 | having count(model)=1 103 | ) q 104 | ``` 105 | 106 | | count of makers | 107 | |-----------------| 108 | | 1 | 109 | 110 | ## 29 111 | 112 | В предположении, что приход и расход денег на каждом пункте приема фиксируется не чаще одного раза в день [т.е. первичный ключ (пункт, дата)], написать запрос с выходными данными (пункт, дата, приход, расход). Использовать таблицы `Income_o` и `Outcome_o`. 113 | 114 | ```sql 115 | select 116 | isnull(i.point, o.point) point 117 | , isnull(i.date, o.date) [date] 118 | , inc 119 | , out 120 | from income_o i full outer join outcome_o o 121 | on i.date=o.date and i.point=o.point 122 | ``` 123 | 124 | | point | date | inc | out | 125 | |--------|--------------------------|-------------|------------| 126 | | 1 | 2001-03-14 00:00:00.000 | NULL | 15348.0000 | 127 | | 1 | 2001-03-22 00:00:00.000 | 15000.0000 | NULL | 128 | | 1 | 2001-03-23 00:00:00.000 | 15000.0000 | NULL | 129 | | 1 | 2001-03-24 00:00:00.000 | 3400.0000 | 3663.0000 | 130 | | 1 | 2001-03-26 00:00:00.000 | NULL | 1221.0000 | 131 | | 1 | 2001-03-28 00:00:00.000 | NULL | 2075.0000 | 132 | | 1 | 2001-03-29 00:00:00.000 | NULL | 2004.0000 | 133 | | 1 | 2001-04-11 00:00:00.000 | NULL | 3195.0400 | 134 | | 1 | 2001-04-13 00:00:00.000 | 5000.0000 | 4490.0000 | 135 | | 1 | 2001-04-27 00:00:00.000 | NULL | 3110.0000 | 136 | | 1 | 2001-05-11 00:00:00.000 | 4500.0000 | 2530.0000 | 137 | | 2 | 2001-03-22 00:00:00.000 | 10000.0000 | 1440.0000 | 138 | | 2 | 2001-03-24 00:00:00.000 | 1500.0000 | NULL | 139 | | 2 | 2001-03-29 00:00:00.000 | NULL | 7848.0000 | 140 | | 2 | 2001-04-02 00:00:00.000 | NULL | 2040.0000 | 141 | | 3 | 2001-09-13 00:00:00.000 | 11500.0000 | 1500.0000 | 142 | | 3 | 2001-09-14 00:00:00.000 | NULL | 2300.0000 | 143 | | 3 | 2001-10-02 00:00:00.000 | 18000.0000 | NULL | 144 | | 3 | 2002-09-16 00:00:00.000 | NULL | 2150.0000 | 145 | 146 | ## 30 147 | 148 | В предположении, что приход и расход денег на каждом пункте приема фиксируется произвольное число раз (первичным ключом в таблицах является столбец code), требуется получить таблицу, в которой каждому пункту за каждую дату выполнения операций будет соответствовать одна строка. 149 | Вывод: `point`, `date`, суммарный расход пункта за день (`out`), суммарный приход пункта за день (`inc`). Отсутствующие значения считать неопределенными (`NULL`). 150 | 151 | ```sql 152 | select 153 | isnull(i.point, o.point) point 154 | , isnull(i.date, o.date) date 155 | , sum(o.out) outcome 156 | , sum(i.inc) income 157 | from income i 158 | full join outcome o 159 | on i.point=o.point and i.date=o.date and i.code=o.code 160 | group by isnull(i.point, o.point), isnull(i.date, o.date) 161 | ``` 162 | 163 | | point | date | outcome | income | 164 | |--------|--------------------------|-------------|------------| 165 | | 1 | 2001-03-14 00:00:00.000 | 15348.0000 | NULL | 166 | | 1 | 2001-03-22 00:00:00.000 | NULL | 30000.0000 | 167 | | 1 | 2001-03-23 00:00:00.000 | NULL | 15000.0000 | 168 | | 1 | 2001-03-24 00:00:00.000 | 7163.0000 | 7000.0000 | 169 | | 1 | 2001-03-26 00:00:00.000 | 1221.0000 | NULL | 170 | | 1 | 2001-03-28 00:00:00.000 | 2075.0000 | NULL | 171 | | 1 | 2001-03-29 00:00:00.000 | 4010.0000 | NULL | 172 | | 1 | 2001-04-11 00:00:00.000 | 3195.0400 | NULL | 173 | | 1 | 2001-04-13 00:00:00.000 | 4490.0000 | 10000.0000 | 174 | | 1 | 2001-04-27 00:00:00.000 | 3110.0000 | NULL | 175 | | 1 | 2001-05-11 00:00:00.000 | 2530.0000 | 4500.0000 | 176 | | 2 | 2001-03-22 00:00:00.000 | 2880.0000 | 10000.0000 | 177 | | 2 | 2001-03-24 00:00:00.000 | NULL | 3000.0000 | 178 | | 2 | 2001-03-29 00:00:00.000 | 7848.0000 | NULL | 179 | | 2 | 2001-04-02 00:00:00.000 | 2040.0000 | NULL | 180 | | 3 | 2001-09-13 00:00:00.000 | 2700.0000 | 3100.0000 | 181 | | 3 | 2001-09-14 00:00:00.000 | 1150.0000 | NULL | 182 | 183 | Another solution 184 | 185 | ```sql 186 | with i as ( 187 | select point, date, sum(inc) inc from income 188 | group by point, date 189 | ) 190 | select 191 | isnull(i.point, o.point) as ppoint 192 | , isnull(i.date, o.date) as ddate 193 | , sum(o.out) 194 | , sum(i.inc) 195 | from i 196 | full join ( 197 | select point, date, sum(out) out from outcome 198 | group by point, date 199 | ) o on i.point=o.point and i.date=o.date 200 | group by isnull(i.point, o.point), isnull(i.date, o.date) 201 | ``` 202 | 203 | ## 32 204 | 205 | Одной из характеристик корабля является половина куба калибра его главных орудий (`mw`). С точностью до 2 десятичных знаков определите среднее значение `mw` для кораблей каждой страны, у которой есть корабли в базе данных. 206 | 207 | ```sql 208 | with w as ( 209 | select country, name, bore from classes c join ships s on c.class=s.class 210 | union 211 | select country, ship, bore from classes c join outcomes o on c.class=o.ship 212 | ) 213 | select 214 | w.country 215 | , ROUND(AVG(w.bore*w.bore*w.bore*0.5), 2) as weight 216 | from w 217 | group by w.country 218 | ``` 219 | 220 | | COUNTRY | WEIGHT | 221 | |-------------|---------| 222 | | Germany | 1687.5 | 223 | | Gt.Britain | 1687.5 | 224 | | Japan | 1886.67 | 225 | | USA | 1897.78 | 226 | 227 | - NOTE из-за приколов с округлением получаем неверное решения для MSSQL. Oracle работает как надо. 228 | 229 | ### Некоторые проверочные/промежуточные запросы 230 | 231 | ```sql 232 | -- Классы кораблей без кораблей 233 | select * from ships s 234 | right join classes c on s.class=c.class 235 | where s.name is null; 236 | 237 | -- Корабли в таблице Outcomes, которых нет в таблице Ships 238 | select * from outcomes o 239 | left join ships s on o.ship=s.name 240 | where s.name is null; 241 | 242 | -- ? 243 | select * from classes c 244 | right join ( 245 | select distinct o.ship from outcomes o 246 | where o.ship not in (select name from ships) 247 | ) o on c.class=o.ship; 248 | 249 | -- ? 250 | select c.* from classes c join ships s on c.class=s.name 251 | union all 252 | select c.* from classes c join outcomes o on c.class=o.ship 253 | where o.ship not in (select name from ships); 254 | ``` 255 | 256 | ### Менее оптимальное решение 257 | 258 | ```sql 259 | with w as ( 260 | select c.*, s.name from classes c join ships s on c.class=s.class 261 | union all 262 | select c.*, o.ship from classes c join ( 263 | select distinct ship from outcomes 264 | where ship not in (select name from ships) 265 | ) o on c.class=o.ship 266 | ) 267 | select 268 | w.country 269 | , ROUND(AVG(w.bore*w.bore*w.bore*0.5), 2) as weight 270 | from w 271 | group by w.country 272 | ; 273 | ``` 274 | 275 | ## 34 276 | 277 | По Вашингтонскому международному договору от начала 1922 г. запрещалось строить линейные корабли водоизмещением более 35 тыс.тонн. Укажите корабли, нарушившие этот договор (учитывать только корабли c известным годом спуска на воду). Вывести названия кораблей. 278 | 279 | ```sql 280 | Select s.name from ships s 281 | join classes c on s.class=c.class 282 | where 283 | s.launched >= 1922 284 | and c.displacement > 35000 285 | and type='bb' 286 | ``` 287 | 288 | | name | 289 | |----------------| 290 | | Iowa | 291 | | Missouri | 292 | | Musashi | 293 | | New Jersey | 294 | | North Carolina | 295 | | South Dakota | 296 | | Washington | 297 | | Wisconsin | 298 | | Yamato | 299 | 300 | ## 35 301 | 302 | В таблице `Product` найти модели, которые состоят только из цифр или только из латинских букв (A-Z, без учета регистра). 303 | Вывод: номер модели, тип модели. (Всё это было бы проще, если бы оно умело в регулярные выражения(?)). 304 | 305 | ```sql 306 | select model, type from product 307 | where 308 | model not like '%[^0-9]%' or model not like '%[^a-z]%' 309 | ``` 310 | 311 | | model | type | 312 | |--------|---------| 313 | | 1121 | PC | 314 | | 1232 | PC | 315 | | 1233 | PC | 316 | | 1260 | PC | 317 | | 1276 | Printer | 318 | | 1288 | Printer | 319 | | ... | | 320 | 321 | ### Промежуточная таблица для тестирования решения 322 | 323 | ```sql 324 | with q as ( 325 | select 'abc' as c union all 326 | select 'ABC' as c union all 327 | select 'ABCabc' as c union all 328 | select '123' as c union all 329 | select '123abc123' as c union all 330 | select 'abc123' as c union all 331 | select '123abc' as c union all 332 | select '1' as c union all 333 | select 'a' as c union all 334 | select NULL as c union all 335 | select '' as c union all 336 | select '_' as c union all 337 | select '%1%' as c union all 338 | select '1%' as c union all 339 | select 'abc123abc' as c union all 340 | select '!!123!! ' as c 341 | ) 342 | ``` 343 | 344 | ### Памятка 345 | 346 | Как перевести синтакс LIKE на человеческий язык 347 | 348 | - LIKE %[0-9]% - a string with digits (string contains digits) 349 | - NOT LIKE %[0-9]% a string without digits (string doesnt contain digits) 350 | - NOT LIKE %[^0-9]% - a string only with digits (string does not contain not digits) 351 | - LIKE %[^0-9]% - string countains not digits 352 | 353 | ## 37 354 | 355 | Найдите классы, в которые входит только один корабль из базы данных (учесть также корабли в Outcomes). 356 | 357 | ```sql 358 | select q.class from ( 359 | select class, name from ships 360 | union 361 | select c.class, o.ship from classes c 362 | join outcomes o on c.class=o.ship 363 | ) q 364 | group by q.class 365 | having count(q.class)=1 366 | ; 367 | ``` 368 | 369 | | class | 370 | |----------| 371 | | Bismarck | 372 | 373 | ## 39 374 | 375 | Найдите корабли, сохранившиеся для будущих сражений; т.е. выведенные из строя в одной битве (damaged), они участвовали в другой, произошедшей позже. 376 | 377 | ```sql 378 | SELECT 379 | distinct o.ship 380 | FROM outcomes o JOIN Battles b ON b.name=o.battle 381 | WHERE 382 | o.result = 'damaged' 383 | and EXISTS( 384 | SELECT * 385 | FROM outcomes o2 JOIN Battles b2 ON b2.name=o2.battle 386 | WHERE 387 | o2.ship=o.ship 388 | and b2.date > b.date 389 | ) 390 | ``` 391 | 392 | | ship | 393 | |------------| 394 | | California | 395 | 396 | Пример плохого условия. "Сохранившиеся для будущих сражений" предполагает по сути ЛЮБОЙ корабль, который участвовал более, чем в 1 сражении. Дальнейшее уточнее условия наконец говорит, что корабль обязательно должен быть поврежден. Т.о. "сохранившиеся для будущих сражений" сбивает с толку и является лишним. 397 | 398 | ```sql 399 | -- Корабли, которые "сохранились для следующей битвы" 400 | -- (т.е. не обязательно поврежденные) 401 | select ship from outcomes o, battles b 402 | where o.battle=b.name 403 | group by ship 404 | having count(ship) > 1 405 | ``` 406 | 407 | ## 41 408 | 409 | Для ПК с максимальным кодом из таблицы PC вывести все его характеристики (кроме кода) в два столбца: 410 | 411 | - название характеристики (имя соответствующего столбца в таблице PC); 412 | - значение характеристики 413 | 414 | - NOTE решение не принимается, "несовпадение данных" (потому, что начальная длина строк в 10 байт оказалась **недостаточной**) 415 | 416 | ```sql 417 | select fields, nullif(value,'x') value from 418 | ( 419 | Select 420 | cast(model as NVARCHAR(50)) as model 421 | , cast (speed as NVARCHAR(50)) as speed 422 | , cast(ram as NVARCHAR(50)) as ram 423 | , cast(hd as NVARCHAR(50)) as hd 424 | , cast(cd as NVARCHAR(50)) as cd 425 | --, cast(price as NVARCHAR(50)) as price 426 | , COALESCE(CAST(price as NVARCHAR(50)),'x') as price 427 | from PC 428 | where code = (Select max(code) from PC) 429 | ) as t 430 | unpivot 431 | ( 432 | value for fields in (model, speed, ram, hd, cd, price) 433 | ) as unp 434 | ``` 435 | 436 | | fields | A | 437 | |---------|--------| 438 | | cd | 50x | 439 | | hd | 20 | 440 | | model | 1233 | 441 | | price | 970.00 | 442 | | ram | 128 | 443 | | speed | 800 | 444 | 445 | ## 41 (NEW) 446 | 447 | Для каждого производителя, у которого присутствуют модели хотя бы в одной из таблиц `PC`, `Laptop` или `Printer`, 448 | определить максимальную цену на его продукцию. 449 | Вывод: 450 | 451 | - имя производителя 452 | - если среди цен на продукцию данного производителя присутствует `NULL`, то выводить для этого производителя `NULL`, иначе максимальную цену. 453 | 454 | ```sql 455 | with m as ( 456 | select model, price, iif(price is null, 1, 0) pn from printer 457 | union all 458 | select model, price, iif(price is null, 1, 0) pn from pc 459 | union all 460 | select model, price, iif(price is null, 1, 0) pn from laptop 461 | ) 462 | select 463 | maker 464 | , case 465 | when sum(pn) > 0 then NULL 466 | else max(price) 467 | end as max_price 468 | from product p join m on p.model=m.model 469 | group by maker 470 | ``` 471 | 472 | ## 45 473 | 474 | Найдите названия всех кораблей в базе данных, состоящие из трех и более слов (например, King George V). 475 | Считать, что слова в названиях разделяются единичными пробелами, и нет концевых пробелов. 476 | 477 | ```sql 478 | select name from ships where name like '_% _% _%' 479 | union 480 | select ship from outcomes where ship like '_% _% _%' 481 | ``` 482 | 483 | | name | 484 | |-----------------| 485 | | Duke of York | 486 | | King George V | 487 | | Prince of Wales | 488 | 489 | ## 46 490 | 491 | Для каждого корабля, участвовавшего в сражении при Гвадалканале (Guadalcanal), вывести название, водоизмещение и число орудий. 492 | 493 | ```sql 494 | with allships as ( 495 | select name, class from ships 496 | union 497 | select ship as name, ship as class from outcomes 498 | where ship not in (select name from ships) 499 | ) 500 | select a.name, displacement, numGuns from allships a 501 | left join classes c on a.class=c.class 502 | join outcomes o on a.name=o.ship and o.battle='Guadalcanal'; 503 | 504 | -- More optimal 505 | SELECT 506 | ship, displacement, numGuns 507 | FROM Outcomes A 508 | LEFT JOIN Ships C ON A.ship = C.name 509 | LEFT JOIN Classes B ON A.ship = B.class OR C.class = B.class 510 | WHERE battle = 'Guadalcanal' 511 | ``` 512 | 513 | | name | displacement | numGuns | 514 | |---------------|---------------|---------| 515 | | California | 32000 | 12 | 516 | | Kirishima | 32000 | 8 | 517 | | South Dakota | 37000 | 12 | 518 | | Washington | 37000 | 12 | 519 | -------------------------------------------------------------------------------- /SELECT(learning stage)/47-61 SELECT learning stage.md: -------------------------------------------------------------------------------- 1 | ## 47 2 | 3 | Пронумеровать строки из таблицы `Product` в следующем порядке: имя производителя в порядке убывания числа производимых им моделей (при одинаковом числе моделей имя производителя в алфавитном порядке по возрастанию), номер модели (по возрастанию). 4 | Вывод: номер в соответствии с заданным порядком, имя производителя (maker), модель (model) 5 | 6 | ```sql 7 | select 8 | ROW_NUMBER() OVER(ORDER BY count desc, maker asc, model asc) rownum 9 | , maker 10 | , model 11 | from ( 12 | -- tmp table with count of models for each maker 13 | select 14 | count(model) over(partition by maker) as count 15 | , p.* 16 | from product p 17 | ) p 18 | ``` 19 | 20 | | rownum | maker | model | 21 | |---------|--------|-------| 22 | | 10 | E | 2112 | 23 | | 11 | E | 2113 | 24 | | 12 | B | 1121 | 25 | | 13 | B | 1750 | 26 | | 14 | D | 1288 | 27 | | 15 | D | 1433 | 28 | | 16 | C | 1321 | 29 | | 1 | A | 1232 | 30 | | 2 | A | 1233 | 31 | | 3 | A | 1276 | 32 | | 4 | A | 1298 | 33 | | 5 | A | 1401 | 34 | | 6 | A | 1408 | 35 | | 7 | A | 1752 | 36 | | 8 | E | 1260 | 37 | | 9 | E | 1434 | 38 | 39 | ## 47 (NEW) 40 | 41 | Определить страны, которые потеряли в сражениях все свои корабли. 42 | 43 | ```sql 44 | with sh as ( 45 | select c.country, s.name from classes c join ships s on c.class=s.class 46 | union 47 | select c.country, o.ship from outcomes o join classes c on c.class=o.ship 48 | ), 49 | shs as( 50 | -- number of sunked ships 51 | select 52 | country 53 | , count(*) as total 54 | from sh 55 | left join outcomes o on sh.name=o.ship 56 | where result = 'sunk' 57 | group by country 58 | ), 59 | sht as ( 60 | -- total number of ships 61 | select 62 | country 63 | , count(*) as total 64 | from sh 65 | group by country 66 | ) 67 | select x.country from sht x join shs y on x.country=y.country 68 | where x.total=y.total 69 | 70 | -- another solution 71 | with sh as ( 72 | select c.country, s.name from classes c join ships s on c.class=s.class 73 | union 74 | select c.country, o.ship from outcomes o join classes c on c.class=o.ship 75 | ) 76 | , a as ( 77 | select 78 | country, name 79 | , case 80 | when result='sunk' then 1 81 | else 0 82 | end as sunk 83 | from sh left join outcomes o on o.ship=sh.name 84 | ) 85 | select country from a 86 | group by country 87 | having count(distinct name)=sum(sunk) 88 | ``` 89 | 90 | ## 48 91 | 92 | Найдите классы кораблей, в которых хотя бы один корабль был потоплен в сражении. 93 | 94 | ```sql 95 | select distinct c.class from outcomes o 96 | left join ships s on o.ship = s.name 97 | join classes c on (o.ship=c.class or s.class=c.class) 98 | where result = 'sunk' 99 | ``` 100 | 101 | | class | 102 | |----------| 103 | | Bismarck | 104 | | Kongo | 105 | 106 | ## 49 107 | 108 | Найдите названия кораблей с орудиями калибра 16 дюймов (учесть корабли из таблицы `Outcomes`). 109 | 110 | ```sql 111 | select name from ships s join classes c on s.class=c.class 112 | where bore=16 113 | union 114 | select ship from outcomes o join classes c on o.ship=c.class 115 | where bore=16 116 | 117 | -- one more solution (more effective) 118 | select 119 | name 120 | from ( 121 | select name, class from ships 122 | union 123 | select ship, ship from outcomes 124 | ) q 125 | join classes c on q.class=c.class 126 | where bore=16 127 | ``` 128 | 129 | | name | 130 | |----------------| 131 | | Iowa | 132 | | Missouri | 133 | | New Jersey | 134 | | North Carolina | 135 | | South Dakota | 136 | | Washington | 137 | | Wisconsin | 138 | 139 | ## 50 140 | 141 | Найдите сражения, в которых участвовали корабли класса Kongo из таблицы `Ships`. 142 | 143 | ```sql 144 | select distinct battle from outcomes o 145 | join ships s on o.ship=s.name 146 | join classes c on c.class=s.class 147 | where c.class='Kongo' 148 | ``` 149 | 150 | | battle | 151 | |-------------| 152 | | Guadalcanal | 153 | 154 | ## 51 155 | 156 | Найдите названия кораблей, имеющих наибольшее число орудий среди всех имеющихся кораблей такого же водоизмещения (учесть корабли из таблицы `Outcomes`). 157 | 158 | ```sql 159 | with sh as ( 160 | -- все корабли и их классы 161 | select name, class from ships 162 | union 163 | select ship, ship from outcomes 164 | ) 165 | select 166 | name 167 | from sh join classes c on sh.class=c.class 168 | where numguns >= all( 169 | select ci.numguns from classes ci 170 | where ci.displacement=c.displacement 171 | -- это на случай (из подсказки) что может быть класс корабля, 172 | -- но при том самого корабля может не быть в бд 173 | and ci.class in (select sh.class from sh) 174 | ) 175 | ``` 176 | 177 | | name | 178 | |-----------------| 179 | | Bismarck | 180 | | California | 181 | | Iowa | 182 | | Missouri | 183 | | Musashi | 184 | | New Jersey | 185 | | North Carolina | 186 | | Ramillies | 187 | | Revenge | 188 | | Royal Oak | 189 | | Royal Sovereign | 190 | | South Dakota | 191 | | Tennessee | 192 | | Washington | 193 | | Wisconsin | 194 | | Yamato | 195 | 196 | ## 52 197 | 198 | Определить названия всех кораблей из таблицы `Ships`, которые могут быть линейным японским кораблем, 199 | имеющим число главных орудий не менее девяти, калибр орудий менее 19 дюймов и водоизмещение не более 65 тыс.тонн 200 | 201 | ```sql 202 | select 203 | s.name 204 | from ships s 205 | join classes c on c.class=s.class 206 | where country='Japan' 207 | and type='bb' and (numguns>=9 or numguns is null) 208 | and (bore<19 or bore is null) 209 | and (displacement<=65000 or displacement is null) 210 | ``` 211 | 212 | | name | 213 | |---------| 214 | | Musashi | 215 | | Yamato | 216 | 217 | ## 53 218 | 219 | Определите среднее число орудий для классов линейных кораблей. 220 | Получить результат с точностью до 2-х десятичных знаков. 221 | 222 | - NOTE способ округления и преобразования типов. Функция `ROUND` не применяется. 223 | 224 | ```sql 225 | select 226 | cast(avg(numguns*1.0) as numeric(6,2)) as "avg numguns" 227 | from classes 228 | where type='bb' 229 | ``` 230 | 231 | | avg numguns | 232 | |-------------| 233 | | 9.67 | 234 | 235 | ## 54 236 | 237 | С точностью до 2-х десятичных знаков определите среднее число орудий всех линейных кораблей (учесть корабли из таблицы `Outcomes`). 238 | 239 | - NOTE как и в прошлой задаче [54][#54], требуется правильно преобразовать тип числа. 240 | 241 | ```sql 242 | select 243 | cast(avg(numguns*1.0) as numeric(6,2)) as "avg numguns" 244 | from ( 245 | -- все корабли(которые есть в базе) и их классы 246 | select name, class from ships 247 | union 248 | select ship, ship from outcomes 249 | ) s 250 | join classes c on s.class=c.class 251 | where type='bb' 252 | ``` 253 | 254 | | avg numguns | 255 | |-------------| 256 | | 9.63 | 257 | 258 | ## 55 259 | 260 | Для каждого класса определите год, когда был спущен на воду первый корабль этого класса. Если год спуска на воду головного корабля неизвестен, определите минимальный год спуска на воду кораблей этого класса. Вывести: класс, год. 261 | 262 | ```sql 263 | select 264 | c.class 265 | , min(launched) "launch year" 266 | from classes c 267 | full join ships s on c.class=s.class 268 | group by c.class 269 | ``` 270 | 271 | | class | launch year | 272 | |-----------------|-------------| 273 | | Bismarck | NULL | 274 | | Iowa | 1943 | 275 | | Kongo | 1913 | 276 | | North Carolina | 1941 | 277 | | Renown | 1916 | 278 | | Revenge | 1916 | 279 | | Tennessee | 1920 | 280 | | Yamato | 1941 | 281 | 282 | ## 56 283 | 284 | Для каждого класса определите число кораблей этого класса, потопленных в сражениях. Вывести: класс и число потопленных кораблей. 285 | 286 | ```sql 287 | select 288 | class 289 | , SUM(CASE WHEN result='sunk' THEN 1 ELSE 0 END) as sunks 290 | from ( 291 | -- все корабли для имеющихся в базе классов кораблей 292 | select c.class, name from classes c 293 | left join ships s on c.class=s.class 294 | union 295 | select class, ship from classes 296 | join outcomes on class=ship 297 | ) as sh 298 | left join outcomes o on sh.name=o.ship 299 | group by class 300 | ``` 301 | 302 | | class | sunks | 303 | |-----------------|-------| 304 | | Bismarck | 1 | 305 | | Iowa | 0 | 306 | | Kongo | 1 | 307 | | North Carolina | 0 | 308 | | Renown | 0 | 309 | | Revenge | 0 | 310 | | Tennessee | 0 | 311 | | Yamato | 0 | 312 | 313 | ## 57 314 | 315 | Для классов, имеющих потери в виде потопленных кораблей и не менее 3 кораблей в базе данных, вывести имя класса и число потопленных кораблей. 316 | 317 | ```sql 318 | select 319 | class 320 | , SUM(CASE WHEN result='sunk' THEN 1 ELSE 0 END) as sunks 321 | from ( 322 | select c.class, name from classes c 323 | join ships s on c.class=s.class 324 | union 325 | select class, ship from classes 326 | join outcomes on class=ship 327 | ) as sh 328 | left join outcomes o on sh.name=o.ship 329 | group by class 330 | having 331 | SUM(CASE WHEN result='sunk' THEN 1 ELSE 0 END) > 0 332 | and (select count(si.name) from ( 333 | select s.name, s.class from ships s 334 | union 335 | select o.ship, o.ship from outcomes o 336 | ) as si 337 | where si.class = sh.class 338 | group by si.class 339 | )>=3 340 | ``` 341 | 342 | | class | sunks | 343 | |--------|-------| 344 | | Kongo | 1 | 345 | 346 | ## 58 347 | 348 | Для каждого типа продукции и каждого производителя из таблицы `Product` c точностью до двух десятичных знаков найти процентное отношение числа моделей данного типа данного производителя к общему числу моделей этого производителя. 349 | Вывод: `maker`, `type`, процентное отношение числа моделей данного типа к общему числу моделей производителя 350 | 351 | ```sql 352 | select distinct 353 | maker, type 354 | -- кол-во моделей каждого типа у каждого производителя 355 | --, count(model) over(partition by maker, type) as mod_type_count 356 | -- общее число моделей для каждого производителя 357 | --, count(model) over(partition by maker) as maker_models_total 358 | , cast(ROUND(( 359 | count(model) over(partition by maker, type))*100.0/ 360 | count(model) over(partition by maker) 361 | ,2) as NUMERIC(5,2)) 362 | as 'mods of type / mods total, %' 363 | from ( 364 | select 365 | pt.maker, pt.type, p.model 366 | from ( 367 | -- Комбинация(1) всех типов моделей 368 | -- и всех производителей 369 | select distinct a.maker, b.type 370 | from product a, product b 371 | ) pt 372 | -- (1) соединяется с моделями. 373 | -- Если производитель не выпускает какой-то тип продукта 374 | -- то такая модель будет NULL 375 | left join product p on pt.maker=p.maker and pt.type=p.type 376 | ) as p 377 | order by maker, type 378 | ``` 379 | 380 | | maker | type | mods of type / mods total, % | 381 | |--------|----------|------------------------------| 382 | | A | Laptop | 28.57 | 383 | | A | PC | 28.57 | 384 | | A | Printer | 42.86 | 385 | | B | Laptop | 50.00 | 386 | | B | PC | 50.00 | 387 | | B | Printer | .00 | 388 | | C | Laptop | 100.00 | 389 | | C | PC | .00 | 390 | | C | Printer | .00 | 391 | | D | Laptop | .00 | 392 | | D | PC | .00 | 393 | | D | Printer | 100.00 | 394 | | E | Laptop | .00 | 395 | | E | PC | 75.00 | 396 | | E | Printer | 25.00 | 397 | 398 | ## 59 399 | 400 | Посчитать остаток денежных средств на каждом пункте приема 401 | для базы данных с отчетностью не чаще одного раза в день. 402 | Вывод: пункт, остаток. 403 | 404 | ```sql 405 | select 406 | coalesce(i.point,o.point) as point 407 | --,coalesce(i.date,o.date) as date 408 | --,sum(coalesce(inc,0)) as total_income 409 | --,sum(coalesce(out,0)) as total_outcome 410 | ,sum(coalesce(inc,0))-sum(coalesce(out,0)) as remain 411 | from income_o i 412 | full join outcome_o o on i.date=o.date and i.point=o.point 413 | group by coalesce(i.point,o.point) 414 | order by 1,2 415 | ``` 416 | 417 | | point | remain | 418 | |--------|------------| 419 | | 1 | 5263.9600 | 420 | | 2 | 172.0000 | 421 | | 3 | 23550.0000 | 422 | 423 | ## 60 424 | 425 | Посчитать остаток денежных средств **на начало дня 15/04/01** 426 | на каждом пункте приема для базы данных с отчетностью 427 | не чаще одного раза в день. Вывод: пункт, остаток. 428 | Замечание. Не учитывать пункты, информации о которых нет до указанной даты. 429 | 430 | ```sql 431 | select 432 | coalesce(i.point,o.point) as point 433 | ,sum(coalesce(inc,0))-sum(coalesce(out,0)) as remain 434 | from income_o i 435 | full join outcome_o o on i.date=o.date and i.point=o.point 436 | -- "на начало дня" значит 437 | -- ДО УКАЗАННОЙ ДАТЫ (раньше указанной даты) 438 | where coalesce(i.date,o.date) < '2001-04-15' 439 | group by coalesce(i.point,o.point) 440 | order by 1,2 441 | ``` 442 | 443 | | point | remain | 444 | |--------|-----------| 445 | | 1 | 6403.9600 | 446 | | 2 | 172.0000 | 447 | 448 | ## 61 449 | 450 | Посчитать остаток денежных средств на всех пунктах приема для базы данных с отчетностью не чаще одного раза в день. 451 | 452 | ```sql 453 | select 454 | sum(coalesce(inc,0))-sum(coalesce(out,0)) as remain 455 | from income_o i 456 | full join outcome_o o on i.date=o.date and i.point=o.point 457 | ``` 458 | 459 | | remain | 460 | |------------| 461 | | 28985.9600 | 462 | -------------------------------------------------------------------------------- /SELECT(learning stage)/62-76 SELECT learning stage.md: -------------------------------------------------------------------------------- 1 | ## 62 2 | 3 | Посчитать остаток денежных средств на всех пунктах приема **на начало дня 15/04/01** для базы данных с отчетностью не чаще одного раза в день. 4 | 5 | ```sql 6 | select 7 | sum(coalesce(inc,0))-sum(coalesce(out,0)) as remain 8 | from income_o i 9 | full join outcome_o o on i.date=o.date and i.point=o.point 10 | where coalesce(i.date,o.date) < '2001-04-15' 11 | ``` 12 | 13 | | remain | 14 | |-----------| 15 | | 6575.9600 | 16 | 17 | ## 63 18 | 19 | Определить имена разных пассажиров, когда-либо летевших на одном и том же месте более одного раза. 20 | 21 | ```sql 22 | select name from passenger 23 | where id_psg in ( 24 | select 25 | p.id_psg 26 | from pass_in_trip p 27 | group by p.id_psg, p.place 28 | having count(*) > 1 29 | ) 30 | ``` 31 | 32 | | name | 33 | |----------------| 34 | | Bruce Willis | 35 | | Mullah Omar | 36 | | Nikole Kidman | 37 | 38 | ## 64 39 | 40 | Используя таблицы `Income` и `Outcome`, для каждого пункта приема определить дни, когда был приход, но не было расхода и наоборот. 41 | Вывод: пункт, дата, тип операции (inc/out), денежная сумма за день. 42 | 43 | ```sql 44 | select 45 | coalesce(i.point,o.point) as point 46 | ,coalesce(i.date,o.date) as date 47 | ,CASE WHEN sum(inc) is not null 48 | THEN 'inc' ELSE 'out' 49 | END as operation 50 | ,CASE WHEN sum(inc) is not null 51 | THEN sum(inc) 52 | ELSE sum(out) 53 | END as money 54 | from income i 55 | full join outcome o on i.date=o.date and i.point=o.point 56 | group by coalesce(i.point,o.point), coalesce(i.date,o.date) 57 | having sum(inc) is null OR sum(out) is null 58 | order by 1,2 59 | ``` 60 | 61 | | point | date | operation | money | 62 | |--------|--------------------------|------------|------------| 63 | | 1 | 2001-03-14 00:00:00.000 | out | 15348.0000 | 64 | | 1 | 2001-03-22 00:00:00.000 | inc | 30000.0000 | 65 | | 1 | 2001-03-23 00:00:00.000 | inc | 15000.0000 | 66 | | 1 | 2001-03-26 00:00:00.000 | out | 1221.0000 | 67 | | 1 | 2001-03-28 00:00:00.000 | out | 2075.0000 | 68 | | 1 | 2001-03-29 00:00:00.000 | out | 4010.0000 | 69 | | 1 | 2001-04-11 00:00:00.000 | out | 3195.0400 | 70 | | 1 | 2001-04-27 00:00:00.000 | out | 3110.0000 | 71 | | 2 | 2001-03-24 00:00:00.000 | inc | 3000.0000 | 72 | | 2 | 2001-03-29 00:00:00.000 | out | 7848.0000 | 73 | | 2 | 2001-04-02 00:00:00.000 | out | 2040.0000 | 74 | | 3 | 2001-09-14 00:00:00.000 | out | 1150.0000 | 75 | 76 | ## 65 77 | 78 | Пронумеровать уникальные пары `{maker, type}` из `Product`, упорядочив их следующим образом: 79 | 80 | - имя производителя (`maker`) по возрастанию; 81 | - тип продукта (`type`) в порядке PC, Laptop, Printer. 82 | 83 | Если некий производитель выпускает несколько типов продукции, то выводить его имя только в первой строке; 84 | остальные строки для ЭТОГО производителя должны содержать пустую строку символов (''). 85 | 86 | ```sql 87 | select 88 | row_number() over(order by maker) as num 89 | -- Выводим {maker} только если 90 | -- это первая строка 91 | ,CASE WHEN mnum=1 THEN maker 92 | ELSE '' 93 | END as maker 94 | ,type 95 | from ( 96 | select 97 | -- Нумеруем {maker, type} для каждого {maker} 98 | row_number() over(partition by maker order by maker, ord) as mnum 99 | ,maker 100 | ,type 101 | from ( 102 | -- Выбираем уникальные {maker, type}, 103 | -- а также создаем доп. столбец {ord} 104 | -- для требуемой сортировки 105 | select 106 | distinct maker, type 107 | ,CASE WHEN LOWER(type)='pc' then 1 108 | WHEN LOWER(type)='laptop' then 2 109 | ELSE 3 110 | END as ord 111 | from product 112 | ) as mto 113 | ) as mtn 114 | ``` 115 | 116 | | num | maker | type | 117 | |------|--------|---------| 118 | | 1 | A | PC | 119 | | 2 | | Laptop | 120 | | 3 | | Printer | 121 | | 4 | B | PC | 122 | | 5 | | Laptop | 123 | | 6 | C | Laptop | 124 | | 7 | D | Printer | 125 | | 8 | E | PC | 126 | | 9 | | Printer | 127 | 128 | ## 66 129 | 130 | Для всех дней в интервале с 01/04/2003 по 07/04/2003 определить число рейсов из Rostov. 131 | Вывод: дата, количество рейсов 132 | 133 | ```sql 134 | -- общая таблицы с рейсами из Ростова 135 | -- в заданном интервале дат 136 | select 137 | * 138 | from trip t join pass_in_trip pt on t.trip_no=pt.trip_no 139 | where town_from='Rostov' 140 | and (date between '01/04/2003' and '07/04/2003') 141 | order by date asc; 142 | 143 | ----------------------- 144 | -- Решение (PostgreSQL) 145 | 146 | with ds as ( 147 | -- Генерация последовательности дат, для Postgre SQL. 148 | -- Проблема к преобразованием дат: 149 | -- синтак вида to_date() выдавал даты с TIMEZONE. 150 | -- Больше подробностей: https://phili.pe/posts/timestamps-and-time-zones-in-postgresql/ 151 | SELECT generate_series( 152 | '01/04/2003'::timestamp, 153 | '07/04/2003'::timestamp, 154 | '1 day' 155 | ) as date 156 | ) 157 | select 158 | ds.date 159 | -- Считаем уникальное время вылета 160 | , count(distinct time_out) 161 | from ds 162 | left join pass_in_trip pt on ds.date=pt.date 163 | left join trip t on pt.trip_no=t.trip_no 164 | and town_from='Rostov' 165 | group by ds.date 166 | ``` 167 | 168 | | date | count | 169 | |----------------------|-------| 170 | | 2003-04-01 00:00:00 | 1 | 171 | | 2003-04-02 00:00:00 | 0 | 172 | | 2003-04-03 00:00:00 | 0 | 173 | | 2003-04-04 00:00:00 | 0 | 174 | | 2003-04-05 00:00:00 | 1 | 175 | | 2003-04-06 00:00:00 | 0 | 176 | | 2003-04-07 00:00:00 | 0 | 177 | 178 | ## 67 179 | 180 | Найти количество маршрутов, которые обслуживаются наибольшим числом рейсов. 181 | Замечания. 182 | 183 | - A - B и B - A считать РАЗНЫМИ маршрутами. 184 | - Использовать только таблицу Trip 185 | 186 | ```sql 187 | with q as ( 188 | -- подзапрос считает кол-во рейсов 189 | -- для каждого направления {town_from, town_to} 190 | select 191 | count(*) as c 192 | from trip 193 | group by town_from, town_to 194 | ) 195 | -- главный запрос считает кол-во направлений 196 | -- которые обслуживаются наибольшим числом рейсов 197 | select count(*) as route_count from q 198 | where c=(select max(c) from q) 199 | ``` 200 | 201 | | route_count | 202 | |-------------| 203 | | 4 | 204 | 205 | ## 68 206 | 207 | Найти количество маршрутов, которые обслуживаются наибольшим числом рейсов. 208 | Замечания: 209 | 210 | - A - B и B - A считать **ОДНИМ И ТЕМ ЖЕ** маршрутом. 211 | - Использовать только таблицу `Trip` 212 | 213 | ```sql 214 | with rc as ( 215 | select 216 | count(*) as route_trips 217 | from trip 218 | group by 219 | case when town_from > town_to 220 | then town_from else town_to 221 | end 222 | ,case when town_from < town_to 223 | then town_from else town_to 224 | end 225 | ) 226 | select count(*) as route_count from rc 227 | where route_trips=(select max(route_trips) from rc); 228 | 229 | -- А еще можно сложить town_from и town_to 230 | -- как строки по аналогичному условию. 231 | ``` 232 | 233 | | route_count | 234 | |-------------| 235 | | 2 | 236 | 237 | - NOTE динозавра вида `CASE WHEN condition THEN expr1 ELSE expr2 END` можно заменить функцией `IIF()`: 238 | 239 | ```sql 240 | IIF(condition, true_expr, false_expr) 241 | ``` 242 | 243 | ## 69 244 | 245 | По таблицам `Income` и `Outcome` для каждого пункта приема найти остатки денежных средств на конец каждого дня, 246 | в который выполнялись операции по приходу и/или расходу на данном пункте. 247 | Учесть при этом, что деньги не изымаются, а остатки/задолженность переходят на следующий день. 248 | Вывод: пункт приема, день в формате "dd/mm/yyyy", остатки/задолженность на конец этого дня. 249 | 250 | ```sql 251 | with q as ( 252 | select 253 | isnull(i.point, o.point) point 254 | , isnull(i.date, o.date) date 255 | , coalesce(sum(i.inc), 0) - coalesce(sum(o.out), 0) balance 256 | from income i 257 | full join outcome o 258 | on i.point=o.point and i.date=o.date and i.code=o.code 259 | group by isnull(i.point, o.point), isnull(i.date, o.date) 260 | ) 261 | select 262 | point 263 | -- 103 means format "dd/mm/yyyy" 264 | , convert(varchar, date, 103) day 265 | , sum(balance) over(partition by point order by date RANGE UNBOUNDED PRECEDING) as rem 266 | from q 267 | order by point,date 268 | ; 269 | 270 | -- another solution 271 | select DISTINCT 272 | point 273 | , convert(varchar, date, 103) day 274 | , sum(inc) over(partition by point order by date RANGE UNBOUNDED PRECEDING) as rem 275 | from ( 276 | select point, date, inc from income 277 | union all 278 | select point, date, -out from outcome 279 | ) q 280 | order by point, date 281 | ; 282 | ``` 283 | 284 | | point | day | rem | 285 | |--------|-------------|-------------| 286 | | 1 | 14/03/2001 | -15348.0000 | 287 | | 1 | 22/03/2001 | 14652.0000 | 288 | | 1 | 23/03/2001 | 29652.0000 | 289 | | 1 | 24/03/2001 | 29489.0000 | 290 | | 1 | 26/03/2001 | 28268.0000 | 291 | | 1 | 28/03/2001 | 26193.0000 | 292 | | 1 | 29/03/2001 | 22183.0000 | 293 | | 1 | 11/04/2001 | 18987.9600 | 294 | | 1 | 13/04/2001 | 24497.9600 | 295 | | 1 | 27/04/2001 | 21387.9600 | 296 | | 1 | 11/05/2001 | 23357.9600 | 297 | | 2 | 22/03/2001 | 7120.0000 | 298 | | 2 | 24/03/2001 | 10120.0000 | 299 | | 2 | 29/03/2001 | 2272.0000 | 300 | | 2 | 02/04/2001 | 232.0000 | 301 | | 3 | 13/09/2001 | 400.0000 | 302 | | 3 | 14/09/2001 | -750.0000 | 303 | 304 | ## 70 305 | 306 | Укажите сражения, в которых участвовало по меньшей мере три корабля одной и той же страны. 307 | 308 | ```sql 309 | select 310 | distinct battle 311 | --, country, count(*) 312 | --, sh.class, sh.name 313 | from ( 314 | -- все орабли и их классы, которые есть в базе 315 | select class, name from ships 316 | union 317 | select ship, ship from outcomes 318 | ) as sh 319 | -- для того, чтобы получить страну 320 | join classes c on c.class=sh.class 321 | -- для того, чтобы получить название битвы 322 | join outcomes o on o.ship=sh.name 323 | group by battle, country 324 | having count(sh.name) >= 3 325 | 326 | -- Вариант из учебника, исправленный и принимаемый в кач-ве решения. 327 | -- Подход к запросу иной: каждая таблица с кораблями 328 | -- соединяется с таблицами классов и битв. 329 | SELECT 330 | DISTINCT battle 331 | FROM ( 332 | SELECT 333 | battle, country 334 | FROM ( 335 | SELECT battle, country 336 | FROM Outcomes INNER JOIN Classes ON ship = class 337 | where ship not in (select name from ships) 338 | UNION ALL 339 | SELECT battle, country 340 | FROM Outcomes o 341 | INNER JOIN Ships s ON o.ship = s.name 342 | INNER JOIN Classes c ON s.class = c.class 343 | ) x 344 | GROUP BY battle, country 345 | HAVING COUNT(*) >= 3 346 | ) y; 347 | ``` 348 | 349 | | battle | 350 | |-------------| 351 | | Guadalcanal | 352 | 353 | ## 71 354 | 355 | Найти тех производителей ПК, все модели ПК которых имеются в таблице `PC`. 356 | 357 | ```sql 358 | -- Через существование 359 | select distinct maker from Product p1 360 | where type='PC' and not exists( 361 | select model from Product p2 362 | where p1.maker=p2.maker and p2.type='PC' and not exists( 363 | select model from pc where p2.model=pc.model 364 | ) 365 | ); 366 | -- operations: 8 367 | 368 | 369 | -- Через группировку: 370 | -- кол-во моделей производителя пк в таблице Product 371 | -- равно кол-ву моделей этого же производителя в таблице PC 372 | select 373 | p.maker 374 | from Product p left join PC on p.model=pc.model 375 | where p.type='pc' 376 | group by maker 377 | having count(p.model)=count(pc.model); 378 | -- operations: 7 379 | 380 | ``` 381 | 382 | | maker | 383 | |-------| 384 | | A | 385 | | B | 386 | 387 | ## 72 388 | 389 | Среди тех, кто пользуется услугами только какой-нибудь одной компании, определить имена разных пассажиров, летавших чаще других. 390 | Вывести: имя пассажира и число полетов. 391 | 392 | ```sql 393 | with q as ( 394 | select 395 | pt.id_psg as id 396 | , count(pt.date) as trip_num 397 | from pass_in_trip pt join trip t on pt.trip_no=t.trip_no 398 | group by pt.id_psg 399 | -- having count(distinct t.id_comp)=1 400 | having max(t.id_comp)=min(t.id_comp) 401 | ) 402 | select name, trip_num 403 | from q join Passenger p on q.id=p.id_psg 404 | where trip_num=(select max(trip_num) from q) 405 | ``` 406 | 407 | | name | trip_num | | | 408 | |---------------|----------|----|---| 409 | | Michael Caine | | | 4 | 410 | | Mullah Omar | | | 4 | 411 | 412 | ## 73 413 | 414 | Для каждой страны определить сражения, в которых не участвовали корабли данной страны. 415 | Вывод: страна, сражение 416 | 417 | ```sql 418 | -- все варианты страна-битва 419 | select country, name as battle from classes, battles 420 | except 421 | -- только реальные варианты страна-битва 422 | select country, battle 423 | from ( 424 | -- все корабли и их классы, которые есть в бд 425 | select class, name as ship_name from ships 426 | union 427 | select ship, ship from outcomes 428 | ) as sh 429 | join Classes c on sh.class=c.class 430 | join Outcomes o on o.ship=sh.ship_name; 431 | 432 | -- еще решение (первое, менее оптимальное) 433 | with sh as ( 434 | select class, name from ships 435 | union 436 | select ship, ship from outcomes 437 | ), 438 | cc as ( 439 | select name, country, battle from Classes c 440 | left join sh on c.class=sh.class 441 | join outcomes o on sh.name=o.ship 442 | ) 443 | select distinct c.country, b.name 444 | from classes c, battles b 445 | where ( 446 | select count(cc.country) from cc 447 | where cc.country=c.country and cc.battle=b.name 448 | )=0 449 | 450 | ``` 451 | 452 | ## 74 453 | 454 | Вывести классы всех кораблей России (Russia). Если в базе данных нет классов кораблей России, вывести классы для всех имеющихся в БД стран. 455 | Вывод: страна, класс 456 | 457 | ```sql 458 | select 459 | country, class 460 | from Classes 461 | -- We can replace A XOR B operation with this 462 | -- (A and !B) OR (!A and B) 463 | where (country='russia' and 'russia'=ANY(select country from Classes)) 464 | OR (country!='russia' and NOT ('russia' = ANY(select country from Classes))) 465 | ; 466 | 467 | -- more optimal solution 468 | SELECT country, class FROM Classes 469 | WHERE country = 470 | CASE WHEN EXISTS ( 471 | SELECT class FROM Classes WHERE country = 'Russia' 472 | ) THEN 'Russia' 473 | ELSE country 474 | END 475 | ``` 476 | 477 | ## 75 478 | 479 | Для каждого корабля из таблицы `Ships` указать название первого по времени сражения из таблицы Battles, 480 | в котором корабль мог бы участвовать после спуска на воду. 481 | 482 | - Если год спуска на воду неизвестен, взять последнее по времени сражение. 483 | - Если нет сражения, произошедшего после спуска на воду корабля, вывести NULL вместо названия сражения. 484 | 485 | Считать, что корабль может участвовать во всех сражениях, которые произошли в год спуска на воду корабля. 486 | 487 | Вывод: имя корабля, год спуска на воду, название сражения 488 | 489 | Замечание: считать, что не существует двух битв, произошедших в один и тот же день. 490 | 491 | ```sql 492 | with q as ( 493 | select 494 | s.name, launched, b.name as b_name 495 | , rank() over(partition by s.name, launched order by date asc) as num 496 | from ships s left join battles b on datepart(yyyy,date)>=s.launched 497 | ) 498 | select 499 | name, launched 500 | , 501 | case 502 | when launched is null 503 | then (select name from battles where date=(select max(date) from battles)) 504 | 505 | when launched is not null and launched>(select datepart(yyyy,max(date)) from battles) 506 | then NULL 507 | 508 | else b_name 509 | end 510 | as battle 511 | from q 512 | where num=1; 513 | 514 | -- Чужое решение, простое и более эффективное 515 | with t as ( 516 | select 517 | name, launched 518 | , ( 519 | select case when s.launched is null then max(date) else min(date) end 520 | from Battles 521 | where datepart(year,date) >= coalesce(s.launched,0) 522 | ) as date 523 | from Ships s 524 | ) 525 | select t.name,t.launched,b.name 526 | from t 527 | left join Battles b on t.date=b.date; 528 | ``` 529 | 530 | ## 76 531 | 532 | Определить время, проведенное в полетах, для пассажиров, летавших всегда на разных местах. 533 | Вывод: имя пассажира, время в минутах. 534 | 535 | ```sql 536 | with pf as( 537 | -- passangers and their places count 538 | select id_psg, count(*) as place_count 539 | from pass_in_trip 540 | group by id_psg, place 541 | ), 542 | pt as( 543 | -- passangers and their trips 544 | -- for those who fly always at different places 545 | select 546 | pt.id_psg, pt.trip_no 547 | , ps.name 548 | , time_out, time_in 549 | -- see this http://www.sql-tutorial.ru/ru/book_datepart_function.html 550 | -- to get clue about time calculation 551 | , CASE when time_out >= time_in 552 | then time_in-time_out + 1440 553 | else time_in-time_out 554 | end as time 555 | from pass_in_trip pt 556 | join passenger ps on ps.id_psg=pt.id_psg 557 | join ( 558 | select 559 | datepart(hh, time_out)*60 + datepart(mi, time_out) time_out 560 | , datepart(hh, time_in)*60 + datepart(mi, time_in) time_in 561 | , trip_no 562 | from trip t 563 | ) as t on t.trip_no=pt.trip_no 564 | -- this can be replaced with MAX() aggregate 565 | where 1=ALL(select place_count from pf where pf.id_psg=pt.id_psg) 566 | ) 567 | select 568 | name, sum(time) fly_time 569 | from pt 570 | group by id_psg, name 571 | ``` -------------------------------------------------------------------------------- /SELECT(learning stage)/77-xx SELECT learning stage.md: -------------------------------------------------------------------------------- 1 | ## 77 2 | 3 | Определить дни, когда было выполнено максимальное число рейсов из 4 | Ростова ('Rostov'). Вывод: число рейсов, дата. 5 | 6 | ```sql 7 | with q as ( 8 | select 9 | count(distinct t.trip_no) as trip_count 10 | , pt.date 11 | from trip t, pass_in_trip pt 12 | where t.trip_no=pt.trip_no 13 | and town_from='Rostov' 14 | group by date 15 | ) 16 | select 17 | trip_count, date 18 | from q 19 | where trip_count=(select max(trip_count) from q) 20 | ; 21 | ``` 22 | 23 | ## 78 24 | 25 | Для каждого сражения определить первый и последний день 26 | месяца, 27 | в котором оно состоялось. 28 | Вывод: сражение, первый день месяца, последний 29 | день месяца. 30 | 31 | Замечание: даты представить без времени в формате "yyyy-mm-dd". 32 | 33 | ```sql 34 | select 35 | name 36 | -- get previous month, then get its end, then add a day to it 37 | -- and you get first day of current month 38 | , DATEADD(day, 1, EOMONTH(DATEADD(month, -1, date))) first_day 39 | , EOMONTH(date) last_day 40 | from battles; 41 | ``` 42 | 43 | ## 79 44 | 45 | Определить пассажиров, которые больше других времени провели в полетах. 46 | Вывод: имя пассажира, общее время в минутах, проведенное в полетах 47 | 48 | - Получить время полета для каждого пассажира 49 | - Выбрать тех, кто налетал максимальное время 50 | 51 | ```sql 52 | with pass_time as ( 53 | select 54 | pt.id_psg 55 | , SUM( 56 | CASE when time_out >= time_in 57 | then datediff(mi, time_out, time_in) + 1440 58 | else datediff(mi,time_out, time_in) 59 | end 60 | ) as trip_time 61 | from pass_in_trip pt 62 | join trip t on t.trip_no=pt.trip_no 63 | group by id_psg 64 | ) 65 | select p.name, trip_time 66 | from pass_time pt join passenger p on pt.id_psg=p.id_psg 67 | -- with ALL query takes MORE steps 68 | -- where trip_time>=ALL(select trip_time from pass_time ) 69 | -- here using MAX is more efficient than ALL 70 | where trip_time=(select max(trip_time) from pass_time ) 71 | ; 72 | ``` 73 | 74 | ## 80 75 | 76 | Найти производителей компьютерной техники, у которых нет моделей ПК, не представленных в таблице PC. 77 | 78 | ```sql 79 | select distinct maker from product 80 | where maker not in ( 81 | -- makers who has PC models which not in PC table 82 | select maker from product 83 | where type='pc' and model not in (select model from pc) 84 | ); 85 | 86 | -- next solution requieres less operations 87 | select maker from product 88 | except 89 | select maker from product 90 | where type='pc' and model not in (select model from pc); 91 | ``` 92 | 93 | ## 81 94 | 95 | Из таблицы `Outcome` получить все записи за тот месяц (месяцы), с учетом года, в котором суммарное значение расхода (out) было максимальным. 96 | 97 | ```sql 98 | with q as( 99 | select 100 | * 101 | , sum(out) over( 102 | partition by year(date), month(date) 103 | ) as month_out 104 | from Outcome o 105 | ) 106 | select code, point, date, out from q 107 | where month_out=(select max(month_out) from q) 108 | ; 109 | ``` 110 | 111 | ## 82 112 | 113 | В наборе записей из таблицы `PC`, отсортированном по столбцу code (по возрастанию) найти среднее значение цены для каждой шестерки подряд идущих ПК. 114 | Вывод: значение code, которое является первым в наборе из шести строк, среднее значение цены в наборе 115 | 116 | ```sql 117 | with q as ( 118 | select 119 | code 120 | , avg(price) over( 121 | order by code DESC 122 | rows between 5 preceding and current row 123 | ) as avg_price 124 | , row_number() over(order by code ASC) as rownum 125 | from pc 126 | ) 127 | select 128 | code, avg_price 129 | from q 130 | where rownum <= (select max(rownum)-5 from q) 131 | ; 132 | 133 | -- costs a little bit less 134 | with tmp as ( 135 | select 136 | code 137 | , price 138 | , row_number() over(order by code ASC) as rownum 139 | from pc 140 | ) 141 | select 142 | code 143 | , (select avg(price) from tmp where rownum between a.rownum and a.rownum+5 144 | ) as avg 145 | from tmp a 146 | where rownum <= (select max(rownum)-5 from q) 147 | ``` 148 | 149 | ## 83 150 | 151 | Определить названия всех кораблей из таблицы `Ships`, которые удовлетворяют, по крайней мере, комбинации любых четырёх критериев из следующего списка: 152 | numGuns = 8 153 | bore = 15 154 | displacement = 32000 155 | type = bb 156 | launched = 1915 157 | class=Kongo 158 | country=USA 159 | 160 | ```sql 161 | with q as ( 162 | select 163 | name 164 | , case numGuns when 8 then 1 else 0 end as a 165 | , case bore when 15 then 1 else 0 end as c 166 | , case displacement when 32000 then 1 else 0 end as b 167 | , case type when 'bb' then 1 else 0 end as d 168 | , case launched when 1915 then 1 else 0 end as e 169 | , case c.class when 'Kongo' then 1 else 0 end as f 170 | , case country when 'USA' then 1 else 0 end as g 171 | from ships s, classes c where s.class=c.class 172 | ) 173 | select name from q where (a+b+c+d+e+f+g)>=4 174 | ``` 175 | 176 | ## 84 177 | 178 | Для каждой компании подсчитать количество перевезенных пассажиров (если они были в этом месяце) по декадам апреля 2003. При этом учитывать только дату вылета. 179 | Вывод: название компании, количество пассажиров за каждую декаду 180 | 181 | ```sql 182 | -- первый монстр 183 | -- cost 0.15383219718933 184 | -- operations 51 185 | with q as ( 186 | select 187 | t.id_comp 188 | , case 189 | when day(date) < 11 then 1 190 | when day(date) < 21 then 2 191 | when day(date) < 32 then 3 192 | end as decade 193 | , count(pt.id_psg) as psg_count 194 | from pass_in_trip pt 195 | join trip t on t.trip_no=pt.trip_no 196 | where year(date)=2003 and month(date)=4 197 | group by t.id_comp, 198 | case 199 | when day(date) < 11 then 1 200 | when day(date) < 21 then 2 201 | when day(date) < 32 then 3 202 | end 203 | ) 204 | select 205 | distinct 206 | name 207 | , coalesce((select top 1 psg_count from q where decade=1 and q.id_comp=c.id_comp), 0) as '1' 208 | , coalesce((select top 1 psg_count from q where decade=2 and q.id_comp=c.id_comp), 0) as '2' 209 | , coalesce((select top 1 psg_count from q where decade=3 and q.id_comp=c.id_comp), 0) as '3' 210 | from q join company c on c.id_comp=q.id_comp 211 | 212 | -- MORE efficient solution 213 | select 214 | c.name 215 | , SUM(iif(day(date)<11, 1, 0)) as d1 216 | , SUM(iif(day(date)<21 and day(date)>10, 1, 0)) as d2 217 | , SUM(iif(day(date)>20, 1, 0)) as d3 218 | from pass_in_trip pt 219 | join trip t on pt.trip_no=t.trip_no 220 | join company c on c.id_comp=t.id_comp 221 | where year(pt.date)=2003 and month(pt.date)=4 222 | group by name 223 | ``` 224 | 225 | ## 85 226 | 227 | Найти производителей, которые выпускают только принтеры или только PC. 228 | При этом искомые производители PC должны выпускать не менее 3 моделей. 229 | 230 | ```sql 231 | -- only printer makers 232 | select maker from product where type='printer' 233 | except 234 | select maker from product where type!='printer' 235 | union ( 236 | -- only PC makers with at least 3 models 237 | select maker from product where type='pc' 238 | group by maker 239 | having count(model) >= 3 240 | except 241 | select maker from product where type!='pc' 242 | ) 243 | ``` 244 | 245 | ## 86 246 | 247 | Для каждого производителя перечислить в алфавитном порядке с разделителем "/" все типы выпускаемой им продукции. 248 | Вывод: `maker`, `список` типов продукции 249 | 250 | ```sql 251 | with m as ( 252 | select 253 | maker 254 | , max(iif(type='laptop', 'Laptop', char(20))) as lt 255 | , max(iif(type='pc', 'PC', char(20))) as pc 256 | , max(iif(type='printer', 'Printer', char(20))) as pr 257 | from product 258 | group by maker 259 | ) 260 | select 261 | maker 262 | , replace( 263 | replace( 264 | replace(lt + '/' + pc + '/' + pr, char(20)+'/', ''), '/'+char(20), '' 265 | ), char(20), '' 266 | ) 267 | as types 268 | from m 269 | ``` 270 | 271 | ## 87 272 | 273 | Считая, что пункт самого первого вылета пассажира является местом жительства, найти не москвичей, которые прилетали в Москву более одного раза. 274 | Вывод: имя пассажира, количество полетов в Москву. 275 | 276 | ```sql 277 | with t as ( 278 | -- passangers and their trips 279 | select pit.date, id_psg, t.* 280 | from pass_in_trip pit 281 | join trip t on pit.trip_no=t.trip_no 282 | ) 283 | , fo as ( 284 | -- get first fly date+time 285 | select id_psg, min(date+time_out) as date_out 286 | from t group by id_psg 287 | ) 288 | , nm as ( 289 | -- those who are not from Moscow 290 | select fo.id_psg 291 | from fo join t on fo.date_out=(t.date+t.time_out) 292 | where town_from!='Moscow' 293 | ) 294 | select 295 | p.name, count(*) 296 | from t join passenger p on t.id_psg=p.id_psg 297 | where town_to='Moscow' 298 | and t.id_psg in (select * from nm) 299 | group by t.id_psg, p.name 300 | having count(*) > 1 301 | ``` 302 | 303 | ## 88 304 | 305 | Среди тех, кто пользуется услугами только одной компании, определить имена разных пассажиров, летавших чаще других. 306 | Вывести: имя пассажира, число полетов и название компании. 307 | 308 | ```sql 309 | with psc as ( 310 | select 311 | pit.id_psg 312 | , count(pit.trip_no) as trip_count 313 | , max(t.id_comp) as id_comp 314 | from pass_in_trip pit 315 | join trip t on pit.trip_no=t.trip_no 316 | group by pit.id_psg 317 | having count(distinct t.id_comp) = 1 318 | ) 319 | select 320 | p.name, p1.trip_count, c.name 321 | from psc p1 322 | join company c on p1.id_comp = c.id_comp 323 | join passenger p on p1.id_psg = p.id_psg 324 | where p1.trip_count = (select max(trip_count) from psc) 325 | 326 | ``` 327 | 328 | 4 exercises left before moving to the next file! --------------------------------------------------------------------------------