Учебник по SQL

OFFSET - от линии x до линии y

ORDER BY Continent ASC, Subregion DESC
ПРИ СМЕЩЕНИИ 29 СТРОК ИЗВЛЕКАЮТСЯ ТОЛЬКО СЛЕДУЮЩИЕ 50 СТРОК;

С НИЧЬЕЙ - заняли последнее место

ВЫБЕРИТЕ 10 ЛУЧШИХ ПРОЦЕНТОВ С ГАЛСТУКАМИ;

COALESCE - 1. не NULL значение

SELECT COALESCE(NULL,NULL,2,5,NULL)
COALESCE(PickedByPersonID,BackorderOrderID);

WHERE - замена символов

WHERE StockItemName LIKE '%ham[^m]%'
WHERE StockItemName LIKE 'a[l-m]%[l-p]'
WHERE CountryName LIKE 'H__[g,d]%'

WHERE IN - включен ли он в список

WHERE PickedByPersonID IN (3,4,17) AND OrderDate >= '2013-01-01' AND OrderDate <= '2013-12-30'

АГРЕГАТНЫЕ ЗАПРОСЫ - AVG(),SUM(), MIN(), MAX(), COUNT(), COUNT_BIG()

SUM(Количество) AS total_pack, AVG(UnitPrice) AS avg_unitprice, COUNT(OrderLineID) AS cnt_orderline

АГГРЕГАТОРЫ - не обрабатывать NULL

SELECT COUNT(ISNULL(PickingCompletedWhen,'1970-01-01'))
SELECT COUNT_BIG(*) FROM Sales.OrderLines;
COUNT(CASE WHEN PickingCompletedWhen IS NULL THEN 1 ELSE NULL END) AS unkown_values

подзапрос - самодостаточный/простой

SELECT OrderId, AVG(Quantity) as avg_perorderid
FROM Sales.OrderLines
GROUP BY OrderId
HAVING AVG(Quantity) > (SELECT AVG(Quantity) AS avg_q
FROM Sales.OrderLines)
ORDER BY avg_perorderid ASC;

SELECT OrderId, (SELECT AVG(Quantity) AS avg_q FROM Sales.OrderLines)
FROM Sales.OrderLines;

корреляционный подзапрос - последний OrderID 

SELECT CustomerId, OrderId, OrderDate
FROM Sales.Orders o1
WHERE OrderID=(SELECT MAX(OrderID))
FROM Sales.Orders o2
WHERE o1.CustomerId=o2.CustomerId)
Порядок по дате заказа DESC;

повторные запуски со списком

SELECT OrderID, CustomerId, OrderDate FROM Sales.Orders
WHERE CustomerID IN (SELECT CustomerID FROM Sales.Customers WHERE CreditLimit IS NULL OR CreditLimit < 2000)
ORDER BY CustomerID ASC;

EXISTS

SELECT DISTINCT CustomerID, CustomerName
FROM Sales.Customers c
WHERE PostalCityID = 33832
AND EXISTS
(SELECT * FROM Sales.Orders o
WHERE o.CustomerID=c.CustomerID);

run-total - gordulo lekerdezes

SELECT CityID, CityName, (SELECT MIN(CityID) FROM Application.Cities c2 WHERE c2.CityID > c1.CityID) AS nextcityid
FROM Application.Cities c1
ORDER BY CityID ASC;
--javitani
SELECT CityID, CityName, LatestRecordedPopulation, LatestRecordedPopulation + (SELECT SUM(LatestRecordedPopulation) FROM Application.Cities c2 WHERE c1.CityID=c2.CityID) AS run_total
FROM Application.Cities c1
WHERE StateProvinceID IN (SELECT StateProvinceID FROM Application.StateProvinces WHERE StateProvinceName='Colorado')
GROUP BY CityID, CityName, LatestRecordedPopulation;

Версия ДЕРВИРОВАННОЙ ТАБЛИЦЫ

SELECT order_count, month_number
FROM (
SELECT MONTH(OrderDate) AS month_number, COUNT(OrderDate) AS order_count
FROM Sales.Orders
GROUP BY MONTH(OrderDate)
) AS derivtable
WHERE order_count > 6000
ORDER BY month_number ASC;

SELECT order_count, month_number
FROM (
SELECT MONTH(OrderDate), COUNT(OrderDate)
FROM Sales.Orders
GROUP BY MONTH(OrderDate)
) AS derivedtable (month_number,order_count)
WHERE order_count > 6000
ORDER BY month_number ASC;

SELECT CustomerID, ORderID, OrderDate
FROM Sales.Orders AS a -- a=schema_name.table_name
INNER JOIN
(
SELECT MAX(OrderDate) AS maxorderdate
FROM Sales.Orders AS b
) AS derivedtable ON a.orderDate=derivedtable.maxorderdate;

CTE

WITH cte_table AS (
SELECT MONTH(OrderDate) AS month_number, COUNT(OrderDate) AS order_count
FROM Sales.Orders
GROUP BY MONTH(OrderDate)
)
SELECT order_count, month_number FROM cte_table
WHERE order_count > 6000
ORDER BY month_number ASC;

СЛУЧАЙ - НОРМА - ПРОИЗВОДНАЯ ТАБЛИЦА - CTE 

ВЫБОР CASE
WHEN 2013=YEAR(OrderDate) THEN '2013y'
WHEN 2014=FORMAT(OrderDate,'yyyy') THEN '2014y'

WHEN OrderDate >= '20160101' AND OrderDate = '20160101' AND OrderDate = '20160101' AND OrderDate = '20160101' AND OrderDate < '2017010101' THEN '2016y'
END as years
FROM Sales.Orders)
SELECT years, COUNT(years)
FROM whatever
GROUP BY years;

JOIN AND - Это будет отношение "где в" и "где".

SELECT *
FROM dbo.Varosok v JOIN dbo.Orszagok o ON v.OrszagID = o.Id AND o.Nev='USA'

Boss Boss JOIN

SELECT subordinate.Nev, braids.Nev, braids.Nev FROM dbo.people as Subordinate
INNER JOIN dbo.People as fonok ON subordinate.FonokId = fonok.Id
LEFT JOIN dbo.People as majorfonok ON fonok.Fonok.FonokId = majorfonok.Id

длина данных

SELECT emp_name,
LEN(emp_name) LEN
, DATALENGTH(emp_name) data_length
FROM dbo.employees;

УНИКАЛЬНЫЙ ИДЕНТИФИКАТОР

CREATE TABLE #testunique(
id UNIQUEIDENTIFIER DEFAULT NEWID()
, name varchar(100)
);

идентификатор автосоздания

DROP TABLE IF EXISTS HR.Employees
CREATE TABLE HR.Employees (
id int IDENTITY(1,1)
, emp_name NVARCHAR(200)
)

СВОЙ ТИП

CREATE TYPE hunschema.hun_mySSN FROM varchar(9);
CREATE TABLE hunschema.testtable (
id INT NOT NULL
, socnumberHUN hunschema.hun_mySSN
);
INSERT INTO hunschema.testtable
VALUES (1, '123456789');

Избежать проблемы автоинкремента и INSERT

SET IDENTITY_INSERT Hr.Employees ON;

INSERT INTO HR.Employees (id,emp_name)
VALUES(666,'firstname');

SET IDENTITY_INSERT Hr.Employees OFF;

OUTPUT вставлен, удален

INSERT INTO HR.Employees (emp_name)
OUTPUT inserted.emp_name INTO HR.Emplog(emp_name)
VALUES ('testname')

УДАЛИТЬ Hr.Employees
OUTPUT deleted.emp_name INTO HR.Emplog
WHERE id=666;

DELETE testlines where orderdate='20130101′ > LOG 

DELETE dbo.testlines
OUTPUT deleted.*
FROM dbo.testlines tol
INNER JOIN dbo.testorders tor ON tor.OrderID=tol.OrderID
WHERE OrderDate='20130101'

ПЕРВИЧНЫЕ КЛЮЧИ, ВНЕШНИЕ КЛЮЧИ, УНИКАЛЬНЫЙ КЛЮЧ, ОГРАНИЧЕНИЯ

PRIMARY KEY

/* v1 */
ALTER TABLE dbo.employees
ALTER COLUMN emp_id int NOT NULL;

ALTER TABLE dbo.employees
ADD CONSTRAINT PK_employee_empid PRIMARY KEY (emp_id);
/* v2 */
ALTER TABLE dbo.employees
ADD emp_id int IDENTITY(1,1) NOT NULL
CONSTRAINT PK_employees_emp PRIMARY KEY;
/* v3 */
GO
CREATE TABLE dbo.testtable4(
id INT NOT NULL PRIMARY KEY
);
/* v4 */
DROP TABLE IF EXISTS dbo.testtable4;
CREATE TABLE dbo.testtable4(
id INT NOT NULL IDENTITY(1,1
, name varchar(50)
, CONSTRAINT PK_testtable4_id PRIMARY KEY(id)
);

ВНЕШНИЙ КЛЮЧ - 1 > N - ПЕРВИЧНЫЙ КЛЮЧ

ALTER TABLE dbo.employees
ADD CONSTRAINT FK_employees_depid FOREIGN KEY(dep_id) REFERENCES dbo.departments(dep_id);

каскадная версия

ALTER TABLE dbo.employee
ADD CONSTRAINT FK_employees_depid FOREIGN KEY(dep_id) REFERENCES dbo.departments(dep_id) ON UPDATE CASCADE; -- ON DELETE CASCADE

таблица переключения - сотрудники и образование - N > M

DROP TABLE IF EXISTS dbo.conn_educations_employees;
CREATE TABLE dbo.conn_educations_employees (
id INT NOT NULL IDENTITY(1,1)
CONSTRAINT PK_conn_eduemp_id PRIMARY KEY
edu_id INT NOT NULL
CONSTRAINT FK_conn_edu_eduid FOREIGN KEY (edu_id) REFERENCES dbo.educations(edu_id)
,emp_id INT NOT NULL
CONSTRAINT FK_conn_edu_empid FOREIGN KEY (edu_id) REFERENCES dbo.employees(emp_id)
);

1 > 1

DROP TABLE IF EXISTS dbo.itusers;
CREATE TABLE dbo.itusers(
id int NOT NULL IDENTITY(1,1)
CONSTRAINT PK_itusers_id PRIMARY KEY
, имя пользователя VARCHAR(50) NOT NULL
CONSTRAINT UK_itsuser_username UNIQUE
, emp_id INT
CONSTRAINT FK_itusers_empid FOREIGN KEY(emp_id) REFERENCES dbo.employees(emp_id)
CONSTRAINT UK_itusers_empid UNIQUE (emp_id)
);

ОДНОЗНАЧНЫЙ КЛЮЧ

ALTER TABLE dbo.employees
ADD CONSTRAINT UK_employees_person UNIQUE (emp_name, birth_date, mother_name);

ПРОВЕРКА ОГРАНИЧЕНИЙ

ALTER TABLE dbo.employees
ADD CONSTRAINT CK_employees_birthdate CHECK ( birth_date > '19600101');

работает ли контрольное ограничение

ALTER TABLE dbo.employees WITH CHECK
CHECK CONSTRAINT CK_employees_contacts;

СТАНДАРТНЫЙ КОНСТРАКТ

ALTER TABLE dbo.employees
ADD CONSTRAINT DK_phonenumber DEFAULT '0690111111' FOR phone_number;

временная таблица sql2017+ - продвинутый sql server 2019 cu18

CREATE TABLE dbo.persondate (
id int IDENTITY(1,1) PRIMARY KEY NOT NULL
, last_name NVARCHAR(50) NOT NULL
, first_name NVARCHAR(50) NOT NULL
, phone_number VARCHAR(22)
, адрес электронной почты VARCHAR(50)
, sysfirstdate DATETIME2 GENERATED ALWAYS AS ROW START NOT NULL
, sysenddate DATETIME2 GENERATED ALWAYS AS ROW END NOT NULL
, PERIOD FOR SYSTEM_TIME(sysfirstdate,sysenddate)
) WITH (
SYSTEM_VERSIONING = ON (HISTORY_RETENTION_PERIOD = 3 MONTHS
, HISTORY_TABLE = dbo.persondate_history)
)
/* временная таблица - удалена*/
SELECT * FROM dbo.persondate_history temp
LEFT JOIN dbo.persondate actual ON temp.id=actual.id
WHERE actual.id IS NULL;

ИНДЕКС

Типы индексов:
- индекс в памяти: oltp
- индекс хранилища колонок
- хранилище рядов: кластеризованное 1 , некластеризованное

типы индексов хранилища рядов:
- кластеризованный индекс - телефонный справочник, полная таблица
- non-clustered: heap - куча, если на таблице нет кластеризованного индекса
индекс, например, какая страница содержит слово "верховенство закона" на странице 120,130

индексный узел: индексный ключ, адрес
Уровень листьев: стол
корень > междоузлия > листовой узел

некластеризованный индекс. B-дерево

уровень листа: ключ индекса, кластеризованный индекс

хорошо для поиска по ключу индекса, но вы не сможете обойтись без кластеризации

КЛАСТЕРИЗОВАННЫЙ ИНДЕКС

DROP INDEX idx_testlines_clustered ON dbo.testlines;
CREATE CLUSTERED INDEX idx_testlines_clustered ON dbo.testlines(OrderLineID DESC);

некластеризованные индексы хороши, если вы составляете запрос, который часто используется, например, where

CREATE CLUSTERED INDEX idx_testlines_clustered ON dbo.testlines(OrderLineID DESC);

CREATE NONCLUSTERED INDEX idx_testlines_stockitemid ON dbo.testlines(StockItemID);

принудительное использование индекса - не используйте

стоимость: 2.24

SELECT OrderLineID, StockItemID, UnitPrice, Quantity
FROM dbo.testlines WITH (INDEX (idx_testlines_stockitemid))
WHERE StockItemID=148;

уровень листьев включен в UniteЦена, количество

DROP INDEX idx_testlines_stockitemid ON dbo.testlines;
CREATE NONCLUSTERED INDEX idx_testlines_stockitemid ON dbo.testlines(StockItemID) INCLUDE (UnitPrice,Quantity);

Фильтрованный индекс

CREATE UNIQUE INDEX idx_testorders_isinprogress ON dbo.testorders (CustomerID,isInProgress) WHERE isInProgress=1;
/*FOREIGN KEY > без индекса ! лучшая практика - некластеризованный индекс, отфильтрованный testdb isHIgh education table-ben*/
/*может быть шедевр - не годится для даты*/
CREATE INDEX idx_testorders_2016 ON dbo.testorders (OrderDate) INCLUDE(CustomerID) WHERE OrderDate >= '20160101' AND OrderDate<'20170101';

Переменные

Пакетная обработка и переменные (GO)
- между двумя GO то, что у вас есть - это партия
- переменные живут только в партии
DECLARE @stockItemId INT;
SET @stockItemId = 2;

DECLARE @maxValidTo DATETIME2 = '2020-02-02';

В качестве условия фильтрации, даже в нескольких запросах

SELECT * FROM Warehouse.StockItems
WHERE StockItemID = @stockItemId;

DECLARE @myStockItemName NVARCHAR(500);

SET @myStockItemName = (SELECT StockItemName FROM Warehouse.StockItems
WHERE StockItemID = @stockItemId);

SELECT @myStockItemName AS MyStockItemName;
/* Может хранить только одно значение */
/* То же, что и выше */
DECLARE @stockItemId INT;
SET @stockItemId = 2;
DECLARE @myStockItemName NVARCHAR(500);
SELECT @myStockItemName=StockItemName FROM Warehouse.StockItems
WHERE StockItemID = @stockItemId;

принимает последнее значение, если оно указано в select

DECLARE @myStockItemName NVARCHAR(500);
SELECT @myStockItemName=StockItemName FROM Warehouse.StockItems;

DECLARE @myStockItemName NVARCHAR(500);
DECLARE @myStockItemColorId NVARCHAR(500);

SELECT @myStockItemName=StockItemName, @myStockItemColorId=ColorID FROM Warehouse.StockItems;

SELECT @myStockItemName=StockItemName, @myStockItemColorId=ColorID
FROM Warehouse.StockItems
ORDER BY StockItemName;

SELECT @myStockItemName AS MyStockItemName;
SELECT @myStockItemColorId AS MyStockColorId;

Если результат запроса отсутствует, переменная принимает значение NULL

SELECT StockItemName FROM Warehouse.StockItems
WHERE StockItemID = 10000000;

DECLARE @myStockItemName NVARCHAR(500);
SET @myStockItemName = (SELECT StockItemName FROM Warehouse.StockItems
WHERE StockItemID = 10000000);
SELECT @myStockItemName AS MyStockItemName;

DECLARE @myStockItemName NVARCHAR(500);
SELECT @myStockItemName=StockItemName FROM Warehouse.StockItems
WHERE StockItemID = 10000000;
SELECT @myStockItemName AS MyStockItemName;

ДАТАТИПЫ

https://learn.microsoft.com/en-us/sql/t-sql/data-types/data-types-transact-sql?view=sql-server-ver16

числовое число против плавающего

float approximator rounds, для больших чисел целая часть может быть не точной, только первые 7 точны, x*2^y => 199*2*5 - физика, статистические расчеты для больших значений, например, десятичных недостаточно
числовой/десятичный, то, что вы получаете, для финансовых вещей числовой, например, цена 1,99

деньги - на самом деле десятичные, только с четырьмя дробными частями, которые фиксированы - вы можете записать валюту, но не хранить ее

DECLARE @mymoney_sm SMALLMONEY = 3148.29,
        @mymoney MONEY = 3148.29;
SELECT CAST(@mymoney_sm AS VARCHAR) AS 'SM_MONEY varchar',
        CAST(@mymoney AS DECIMAL) AS 'MONEY DECIMAL';

datetime против datetime2

DECLARE @ido_datetime DATETIME = GETDATE();
DECLARE @ido_datetime2 DATETIME2 = GETDATE();

SELECT @ido_datetime, @ido_datetime2;

datetimeoffset

DECLARE @ido_datetimeoffset datetimeoffset = GETDATE();
DECLARE @ido_datetimeoffset2 datetimeoffset = SYSDATETIMEOFFSET()
SELECT @ido_datetime, @ido_datetimeoffset, @ido_datetimeoffset2;

DECLARE @mostaniIdoUTC datetime2=GETUTCDATE();
SELECT @ido_datetime, @ido_datetimeoffset, @ido_datetimeoffset2, @mostaniIdoUTC;

char против varchar

DECLARE @mychar CHAR(3)='test'
SELECT @mychar;

DECLARE @mychar CHAR(3)='te'
SELECT @mychar;

DECLARE @mychar CHAR(3)='te'
SELECT LEN(@mychar);
SELECT DATALENGTH(@mychar);

DECLARE @myvarchar VARCHAR(3)='test'
SELECT @myvarchar;

DECLARE @myvarchar VARCHAR(3)='te'
SELECT @myvarchar;

DECLARE @myvarchar VARCHAR(3)='te'
SELECT LEN(@myvarchar);
SELECT DATALENGTH(@myvarchar);

GO

nchar против nvarchar

DECLARE @mychar NCHAR(3)='test'
SELECT @mychar;

DECLARE @mychar NCHAR(3)='te'
SELECT @mychar;

DECLARE @mychar NCHAR(3)='te'
SELECT LEN(@mychar);
SELECT DATALENGTH(@mychar);

DECLARE @myvarchar NVARCHAR(3)='test'
SELECT @myvarchar;

DECLARE @myvarchar NVARCHAR(3)='te'
SELECT @myvarchar;

DECLARE @myvarchar NVARCHAR(3)='te'
SELECT LEN(@myvarchar);
SELECT DATALENGTH(@myvarchar);

бинарный

DECLARE @myBinary BINARY(10) = 0x00A1B2C3;
SELECT @myBinary;

перевести символы юникода в ascii

DECLARE @mychar CHAR(3) = 'Győr';
DECLARE @mychar2 CHAR(3) = 'Győr';
SELECT @mychar, @mychar2, LEN(@mychar), DATALENGTH(@mychar),LEN(@mychar2), DATALENGTH(@mychar2);

слишком высокие значения

DECLARE @chunk SMALLINT = 32768;
SELECT @darabszam;

 неявное преобразование

https://learn.microsoft.com/en-us/sql/t-sql/data-types/data-type-conversion-database-engine?view=sql-server-ver16

CAST против CONVERT

DECLARE @chunk SMALLINT = 32764;
SELECT @darabszam;
SELECT CAST(@darabszam AS nvarchar)+'?';

DECLARE @ma DATETIME2 =GETUTCDATE();

SELECT @ma,CONVERT(nvarchar,@ma,1)

TRY_CAST против TRY_CONVERT

DECLARE @chunk SMALLINT = 32764;
SELECT @darabszam;
SELECT TRY_CAST(CAST(@darabszam AS nvarchar)+'?' AS INT);

ФОРМАТ

DECLARE @ma DATETIME2 =GETUTCDATE();

SELECT @ma,FORMAT(@ma,'yyyy. MM. dd.')

DECLARE @datum DATETIME2 = '2022-02-15', @datum2 DATETIME2;
SET @datum2 =CONVERT(DATETIME2, '2/15/2022',101)
SELECT @datum,@datum2

DECLARE @float FLOAT = 2.0 / 5;
SELECT @float,FORMAT(@float,'N2')

Цифровые операции: ОБРЕЗКА, ОБРЕЗАТЬ, ОБРЕЗАТЬ, ОБРЕЗАТЬ

Здесь Лен не смотрит на конец текста:

SELECT TRIM(' a '),LEN(' a '), LEN(TRIM(' a '));
SELECT LTRIM(' a '),LEN(' a '), LEN(LTRIM(' a '));
SELECT RTRIM(' a '),LEN(' a '), LEN(RTRIM(' a '));

SELECT TRIM(' a '),LEN(' a '), LEN(TRIM(' a '));
SELECT LTRIM(' a '),DATALENGTH(' a '), DATALENGTH(LTRIM(' a '));
SELECT RTRIM(' a '),LEN(' a '), LEN(RTRIM(' a '));

ВЕРХНИЙ, НИЖНИЙ

SELECT UPPER('Акцент')
SELECT LOWER('ПРЕДЛОЖЕНИЕ')

РЕПЛИЦИРОВАТЬ, ОСТАВИТЬ, ОБРАТИТЬ, ЗАМЕНИТЬ

SELECT REPLICATE('ho',3)
SELECT LEFT('HELLO WORLD', 5)

SELECT REVERSE('hello')

SELECT REPLACE('HELLO world', 'World', 'JOE')

UPDATE Warehouse.StockItems
SET StockItemName = REPLACE(StockItemName,'StockItemName', 'launcher')

Операции с датой времени: DATEDIFF

SELECT ValidFrom, '2022-01-01 00:00', DATEDIFF(yyyy, ValidFrom,'2022-01-01 00:00')
FROM Warehouse.StockItems

SELECT ValidFrom, '2022-01-01 00:00', DATEDIFF(s, ValidFrom,'2022-01-01 00:00')
FROM Warehouse.StockItems

SELECT ValidFrom, '2022-01-01 00:00', DATEDIFF(s, ValidFrom,'2022-01-01 00:00')
FROM Warehouse.StockItems

SELECT DATEDIFF(s,'2022-01-01 00:00','2022-01-01 05:01')

Округление

SELECT ROUND(123.45,0)
SELECT ROUND(123.55,0)
SELECT ROUND(123.55,1)

ВРЕМЕННАЯ ТАБЛИЦА, ТАБЛИЧНАЯ ПЕРЕМЕННАЯ

временная таблица

существует только в сессии, хранится в tempdb, слишком большая tempdb для многих, для хранения запросов таблиц, преимущество: фильтрованные данные, более быстрые запросы, меньше кода, производительность

CREATE TABLE #myTempTable
(
	Id уникальный идентификатор первичный ключ некластеризованный,
	name NVARCHAR(100) NOT NULL
);

Global temp talbe: сохраняется между сессиями, после завершения всех запросов и сессий он завершается

CREATE TABLE ##globalTempTable
(
	Id уникальный идентификатор первичный ключ некластеризованный,
	name NVARCHAR(100) NOT NULL
);
DROP TABLE ##globalTempTable;

Табличная переменная: существует только в пакетном режиме, не идеальна для большого количества данных

DECLARE @myTableVariable TABLE (
	Id uniqueidentifier первичный ключ некластеризованный,
	name NVARCHAR(100) NOT NULL
);
-- создание общей доски
ИСПОЛЬЗУЙТЕ WideWorldImporters;
GO

SELECT COUNT(*) FROM Sales.Orders;
-- стоимость 0,39
SELECT COUNT(*) FROM Sales.OrderLines;

SELECT TOP 1 * FROM Sales.OrderLines;

CREATE TABLE #Temp_OrderLines
(
	OrderLineID int PRIMARY KEY,
	StockItemID int NOT NULL, --/*FOREIGN KEY*/ References Warehouse.StockItems(StockItemID), -не делайте этого, потому что у него нет имени, вам придется найти имя
	ОПИСАНИЕ NVARCHAR(1000),
	Количество int NOT NULL
);
-- 164 id товара на складе
INSERT INTO #Temp_OrderLines(OrderLineID,StockItemID,[DESCRIPTION],Quantity)
SELECT OrderLineID,StockItemID,[Description],Quantity
FROM Sales.OrderLines
WHERE StockItemID=164;

SELECT * FROM #Temp_OrderLines;
--0.013 стоимость
SELECT COUNT(*) FROM #Temp_OrderLines;

INSERT INTO #Temp_OrderLines(OrderLineID,StockItemID,[DESCRIPTION],Quantity)
VALUES (10000,1,'Тест', 1);

SELECT COUNT(*) FROM #Temp_OrderLines;

DECLARE @myTableVariable TABLE (
	Id uniqueidentifier первичный ключ некластеризованный,
	Name NVARCHAR(100) NOT NULL
);

INSERT INTO @myTableVariable (Id,Name) VALUES (NEWID(), 'Teszt');

SELECT * FROM @myTableVariable;

USE WideWorldImporters;

/*
    Объявление типа таблицы
*/
DROP TYPE IF EXISTS mytable;
CREATE TYPE mytable AS table
(
    id int NOT NULL,
	name NVARCHAR(100) NOT NULL
);

/*
    С этого момента вам не нужно объявлять переменные таблицы
    с информацией о столбце, просто ссылайтесь на тип.
*/
DECLARE @t AS mytable;
INSERT INTO @t VALUES(1,'HELLO WORLD');

SELECT * FROM @t;


CREATE TABLE dbo.testtable AS mytable; -- нет

/*это да*/
ВЫБРАТЬ *
INTO dbo.testtable
из @t
WHERE 1 = 2; -столбцы работают, но не строки


SELECT * FROM dbo.testtecske;

ЭЛЕМЕНТЫ ПОТОКА УПРАВЛЕНИЯ (IF, WHILE)

разветвление кода

ЕСЛИ... ИЛИ
ЕСЛИ выражения

- трехзначная логика: TRUE, FALSE, UNKNOWN (NULL)
простой пример

DECLARE @year SMALLINT;
SET @year = 2022;

ЕСЛИ @year = YEAR(GETDATE())
BEGIN
PRINT 'Match! Текущий год: ' + CAST(@year AS varchar(5));
END
ELSE
BEGIN
    PRINT 'НЕТ СОВПАДЕНИЯ! Переменная THE Year:' + CAST(@year AS varchar(5));
    PRINT 'НЕТ СОВПАДЕНИЙ! текущий год:' + CAST(YEAR(GETDATE()) AS varchar(5));
END

DECLARE @year SMALLINT;
SET @year = (SELECT YEAR(MAX(OrderDate)) FROM WideWorldImporters.Sales.Orders);

IF @year = YEAR(GETDATE())
BEGIN
PRINT 'Совпадение! Текущий год: ' + CAST(@year AS varchar(5));
END
ELSE
BEGIN
    PRINT 'НЕТ СОВПАДЕНИЯ! Переменная THE Year:' + CAST(@year AS varchar(5));
    PRINT 'НЕТ СОВПАДЕНИЙ! текущий год:' + CAST(YEAR(GETDATE()) AS varchar(5));
END

NULL обработка вложенных элементов if

DECLARE @year SMALLINT;
SET @year = (SELECT YEAR(MAX(OrderDate)) FROM WideWorldImporters.Sales.Orders WHERE OrderID=500000);

IF @year = YEAR(GETDATE())
BEGIN
PRINT 'Совпадение! Текущий год: ' + CAST(@year AS varchar(5));
END
ELSE
BEGIN
    ЕСЛИ @year НЕ NULL
    BEGIN
    PRINT 'НЕТ СОВПАДЕНИЯ! Переменная THE Year:' + CAST(@year AS varchar(5));
    PRINT 'НЕТ СОВПАДЕНИЙ! текущий год:' + CAST(YEAR(GETDATE()) AS varchar(5));
    END
    ELSE
    BEGIN
    PRINT 'Значение @year равно NULL'
    END
END
GO

Активная БД, она только для ваших собственных, другие сессии все еще могут использовать ее, убейте ее

DECLARE @year SMALLINT;
SET @year = 2023;
ЕСЛИ @year = YEAR(GETDATE())
BEGIN
    USE WideWorldImporters
END

Сложное выражение if

последний день последнего месяца = today-20 || today is IN (monday, tuesday, friday) || today = 2023-02-20
истинный или ложный

PRINT DAY(GETDATE())
PRINT EOMONTH(GETDATE(),-1); -- февраль
PRINT DATEDIFF(dd, GETDATE(),1 )
PRINT CAST(DATEADD(day, -20,GETDATE()) AS date);
PRINT DATENAME(dw,GETDATE())
PRINT GETDATE()

IF EOMONTH(GETDATE(),-1)= CAST(DATEADD(day, -20,GETDATE()) AS date) OR DATENAME(dw,GETDATE()) IN ('Monday','Tuesday','Friday') OR CAST(GETDATE() AS date) = '2023-02-20'
BEGIN
PRINT 'ЭТО ПРАВДА!
END

/*testcase-2*/

IF EOMONTH('2023-03-20',-1)= CAST(DATEADD(day, -20,GETDATE()) AS date) OR DATENAME(dw,'2023-02-19') IN ('Monday','Tuesday','Friday') OR CAST(GETDATE() AS date) = '2023-02-20'
BEGIN
PRINT 'ЭТО ПРАВДА!
PRINT 'ЭТО ВСЕ ЕЩЕ ПРАВДА!
END

/*тестовый пример 3*/

IF EOMONTH('2023-03-20',-1)= CAST(DATEADD(day, -20,GETDATE()) AS date) OR DATENAME(dw,'2023-02-19') IN ('Monday','Tuesday','Friday') OR CAST(GETDATE() AS date) = '2023-01-28'
BEGIN
PRINT 'ЭТО ПРАВДА!
PRINT 'ЭТО ВСЕ ЕЩЕ ПРАВДА!
END
ELSE
BEGIN
ВЫВЕСТИ 'НЕПРАВДА'
END
DECLARE @yeara SMALLINT=2015, @yearb SMALLINT;
 SELECT @yearb;

ЕСЛИ СУЩЕСТВУЕТ

Используйте WideWorldImporters;
GO

DECLARE @yeara SMALLINT=2015, @yearb SMALLINT=2023;

IF EXISTS (SELECT OrderID FROM SAles.Orders WHERE YEAR(OrderDate) IN (@yeara,@yearb)
)
BEGIN
PRINT 'Есть заказы в ' + CAST(@yeara как VARCHAR(5))+ ' или в '+CAST(@yearb как VARCHAR(5))
END
ELSE
BEGIN
    PRINT 'Нет заказов в ' + CAST(@yeara как VARCHAR(5))+ ' или в '+CAST(@yearb как VARCHAR(5))
END ;
/* Та же задача с небольшим добавлением NULL*/
DECLARE @yeara SMALLINT=2015, @yearb SMALLINT;
 SELECT @yearb;

IF EXISTS (SELECT OrderID FROM SAles.Orders WHERE YEAR(OrderDate) IN (@yeara,@yearb)
)
BEGIN
PRINT 'Есть заказы в ' + CAST(@yeara как VARCHAR(5))+ ' или в '+CAST(@yearb как VARCHAR(5))
END
ELSE
BEGIN
    PRINT 'Нет заказов в ' + CAST(@yeara как VARCHAR(5))+ ' или в '+CAST(@yearb как VARCHAR(5))
END ;

GO
/* ОБРАБОТКА НУЛЕЙ*/

DECLARE @yeara SMALLINT=2015, @yearb SMALLINT;
 SELECT @yearb;

IF (@yeara IS NULL OR @yearb IS NULL)
BEGIN
PRINT 'Обнаружено значение(я) UNKNOWN';

END
ELSE
BEGIN
IF EXISTS (SELECT OrderID FROM SAles.Orders WHERE YEAR(OrderDate) IN (@yeara,@yearb) AND (@yeara IS NOT NULL OR @yearb IS NOT NULL)
)
BEGIN
PRINT 'Есть заказы в ' + CAST(@yeara как VARCHAR(5))+ ' или в '+CAST(@yearb как VARCHAR(5));
END
ELSE
BEGIN
    PRINT 'Нет заказов в ' + CAST(@yeara как VARCHAR(5))+ ' или в '+CAST(@yearb как VARCHAR(5));
END
END;

LOOPS

синтаксис:

WHILE (выражение)
НАЧАЛО

КОНЕЦ
(ПРОДОЛЖАТЬ, ПРЕРЫВАТЬ, ИДТИ 1000)

DROP TABLE IF EXISTS dbo.t1, dbo.t2, dbo.t3;
CREATE TABLE dbo.t1 (num int);
CREATE TABLE dbo.t2 (num int IDENTITY(1,1));
CREATE TABLE dbo.t3 (num int IDENTITY(1,1));

/* простой цикл вставки INSERT 1-1000 > dbo.t1 метод 1*/

DECLARE @cnt int=1;

WHILE @cnt 1000 кроме 5 С колонкой автоинкремента (метод 2)
 CONTINUE IF @cnt=5
*/

SET IDENTITY_INSERT dbo.t2 ON;

DECLARE @cnt int=0;
WHILE (@cnt < 1001)
BEGIN
    SET @cnt +=1;
    IF @cnt=5 CONTINUE;
    INSERT INTO dbo.t2 (num) VALUES (@cnt);
END

SELECT num FROM dbo.t2;

SET IDENTITY_INSERT dbo.t2 OFF;

/*
прервать цикл на @cnt=5
*/
TRUNCATE TABLE dbo.t2;

SET IDENTITY_INSERT dbo.t2 ON;

DECLARE @cnt int=0;
WHILE (@cnt  dbo.t3 метод 3
*/
INSERT INTO dbo.t3 DEFAULT VALUES;
GO 1000

SELECT * FROM dbo.t3;

цикл с задержкой

DECLARE @cnt int=1;

WHILE @cnt <= 10
BEGIN

    PRINT 'Значение счетчика равно '+CAST(@cnt AS varchar(5));
    ОЖИДАНИЕ ЗАДЕРЖКИ '00:00:01'
     SET @cnt+= 1;
   

END

/*ЭТО ХОРОШО*/

DECLARE @cnt int=1;

WHILE @cnt <= 10
BEGIN

    RAISERROR('Значение счетчика равно %d',0,1,@cnt) WITH NOWAIT;
    WAITFOR DELAY '00:00:01'
     SET @cnt+= 1;
END

@@ROWCOUNT

считает последнее утверждение

DECLARE @rows int;
UPDATE #mytemp
SET Количество=1000
WHERE Quantity>100;
SET @rows=@@ROWCOUNT;
Если @rows > 0
BEGIN
    SELECT CAST(@rows as varchar(5)) + 'строки были обновлены'
END
ELSE
BEGIN
SELECT 'Ни одна строка не была обновлена'
END

РАБОТА С ОШИБКАМИ

Типы ошибок:
- ошибка преобразования типа данных
- ошибка оценки (ошибка оценки)
- синтаксическая ошибка > не может быть обработана в t-sql
- логическая ошибка
- ошибка разрешения имени > не может быть обработана в t-sql
- ошибка нарушения ограничений
- ошибка при срабатывании

количество -1000

Зачем мы пишем обработку ошибок?
- Я не хочу ломать код
- Я хотел бы зарегистрировать ошибку
- выдавать код ошибки в более удобоваримой форме

Как?
- не потеряйте код
- или избавиться от кода, чтобы избежать серьезных проблем

Методы устранения неполадок:
- структурированная обработка ошибок с помощью: TRY > CATCH
- @@ERROR встроенная функция, не использовать

09:17:15Начало выполнения запроса на строке 42
Msg 8134, Уровень 16, Состояние 1, Строка 1
Возникла ошибка деления на ноль.
Общее время выполнения: 00:00:00.013

Msg 8134 > код ошибки (id)
Уровень > Уровни тяжести
Состояние > статус
Строка > строка, где находится ошибка

пример деления на ноль

ПРИНТ 10/0;

ПОПРОБУЙ... ПОЙМАЙ

НАЧНИТЕ ПОПЫТКУ
    ПРИНТ 10/0;
ЗАВЕРШИТЬ ПОПЫТКУ
НАЧАЛО СЛУЧАЯ
    PRINT '0 - тупой!';
END CATCH;

встроенные функции обработки ошибок

PRINT 'Произошла ошибка!';
PRINT 'Номер ошибки: ' + CAST(ERROR_NUMBER() AS nvarchar(10));
PRINT 'Сообщение об ошибке: ' + ERROR_MESSAGE();
PRINT 'Состояние ошибки: ' + CAST(ERROR_STATE() AS nvarchar(10));
PRINT 'Серьезность ошибки: ' + CAST(ERROR_SEVERITY() AS nvarchar(10));
PRINT 'Строка ошибки: ' + CAST(ERROR_LINE() AS nvarchar(10));
PRINT 'Модуль ошибки: ' + COALESCE(ERROR_PROCEDURE(),");

НАЧНИТЕ ПОПЫТКУ
    ПРИНТ 10/0;
ЗАВЕРШИТЬ ПОПЫТКУ
НАЧАЛО СЛУЧАЯ
    PRINT 'Произошла ошибка!';
    PRINT 'Номер ошибки: ' + CAST(ERROR_NUMBER() AS nvarchar(10));
    PRINT 'Сообщение об ошибке: ' + ERROR_MESSAGE();
    PRINT 'Состояние ошибки: ' + CAST(ERROR_STATE() AS nvarchar(10));
    PRINT 'Серьезность ошибки: ' + CAST(ERROR_SEVERITY() AS nvarchar(10));
    PRINT 'Строка ошибки: ' + CAST(ERROR_LINE() AS nvarchar(10));
    PRINT 'Модуль ошибки: ' + COALESCE(ERROR_PROCEDURE(),'');
END CATCH;

PRINT 'Модуль ошибки: ' + COALESCE(ERROR_PROCEDURE(),");

   НАЧАТЬ ПОПЫТКУ
   EXEC dbo.badprocedure
   ЗАВЕРШИТЬ ПОПЫТКУ
   НАЧАЛО КАТЧА

   КОНЕЦ КАТЧА

ошибка восстановления имени

НАЧАТЬ ПОПЫТКУ
    DECLARE @unitprice INT=0;
    SELECT * FROM dbo.notavailable;

    PRINT 10/@unitprice;
КОНЕЦ ПОПЫТКИ
BEGIN CATCH
    PRINT 'Произошла ошибка!';
    PRINT 'Номер ошибки: ' + CAST(ERROR_NUMBER() AS nvarchar(10));
    PRINT 'Сообщение об ошибке: ' + ERROR_MESSAGE();
    PRINT 'Состояние ошибки: ' + CAST(ERROR_STATE() AS nvarchar(10));
    PRINT 'Серьезность ошибки: ' + CAST(ERROR_SEVERITY() AS nvarchar(10));
    PRINT 'Строка ошибки: ' + CAST(ERROR_LINE() AS nvarchar(10));
    PRINT 'Модуль ошибки: ' + COALESCE(ERROR_PROCEDURE(),'');
END CATCH;

обработка ошибок разрешения имен модулем

GO
CREATE OR ALTER PROCEDURE dbo.badprocedure
AS
    SELECT * FROM dbo.notavailable;
GO
EXEC dbo.badprocedure;
BEGIN TRY
    DECLARE @unitprice int=0;
    EXEC dbo.badprocedure;
    PRINT 10/@unitprice;
КОНЕЦ ИСПЫТАНИЯ
BEGIN CATCH
    PRINT 'Произошла ошибка!';
    PRINT 'Номер ошибки: ' + CAST(ERROR_NUMBER() AS nvarchar(10));
    PRINT 'Сообщение об ошибке: ' + ERROR_MESSAGE();
    PRINT 'Состояние ошибки: ' + CAST(ERROR_STATE() AS nvarchar(10));
    PRINT 'Серьезность ошибки: ' + CAST(ERROR_SEVERITY() AS nvarchar(10));
    PRINT 'Строка ошибки: ' + CAST(ERROR_LINE() AS nvarchar(10));
    PRINT 'Модуль ошибки: ' + COALESCE(ERROR_PROCEDURE(),'');
END CATCH;

ошибка отрицательной цены единицы продукции путем обработки THROW

НАЧАТЬ ПОПЫТКУ
    DECLARE @unitprice INT=-20;
    ЕСЛИ @unitprice < 0
    BEGIN
    -- THROW допустимый диапазон начинается с 50000, уровень серьезности 16
        THROW 55555, 'Значение параметра @unitprice меньше нуля!', 1;
    END
    PRINT 'SOR'
END TRY
BEGIN CATCH
    PRINT 'Произошла ошибка!';
    PRINT 'Номер ошибки: ' + CAST(ERROR_NUMBER() AS nvarchar(10));
    PRINT 'Сообщение об ошибке: ' + ERROR_MESSAGE();
    PRINT 'Состояние ошибки: ' + CAST(ERROR_STATE() AS nvarchar(10));
    PRINT 'Серьезность ошибки: ' + CAST(ERROR_SEVERITY() AS nvarchar(10));
    PRINT 'Строка ошибки: ' + CAST(ERROR_LINE() AS nvarchar(10));
    PRINT 'Модуль ошибки: ' + COALESCE(ERROR_PROCEDURE(),'');
END CATCH;

THROW in CATCH - я ломаю код, но сначала я удаляю причину ошибки

НАЧАТЬ ПОПЫТКУ
    DECLARE @unitprice INT=-20;
    ЕСЛИ @unitprice < 0
    BEGIN
    -- THROW допустимый диапазон начинается с 50000, уровень серьезности 16
        THROW 55555, 'Значение параметра @unitprice меньше нуля!', 1;
    END
    PRINT 'SOR'
END TRY
BEGIN CATCH
    PRINT 'Произошла ошибка!';
    PRINT 'Номер ошибки: ' + CAST(ERROR_NUMBER() AS nvarchar(10));
    PRINT 'Сообщение об ошибке: ' + ERROR_MESSAGE();
    PRINT 'Состояние ошибки: ' + CAST(ERROR_STATE() AS nvarchar(10));
    PRINT 'Серьезность ошибки: ' + CAST(ERROR_SEVERITY() AS nvarchar(10));
    PRINT 'Строка ошибки: ' + CAST(ERROR_LINE() AS nvarchar(10));
    PRINT 'Модуль ошибки: ' + COALESCE(ERROR_PROCEDURE(),'');
    SELECT CAST(@unitprice AS NVARCHAR(100)) + ' значение цены единицы товара вызывает ошибку';
    ОТКАЗ;
END CATCH;

RAISE ERROR - в catch не ломает код

НАЧАТЬ ПОПЫТКУ
    DECLARE @myvar int;

    SET @myvar = -1;

    /* выброс ошибки с аргументом, если значение недопустимо */
    IF @myvar < 0
        RAISERROR ('Значение параметра @myvar равно %d!', 16, 1, @myvar);

    PRINT 'Эта строка не выполняется!'
END TRY
BEGIN CATCH
    PRINT 'Произошла ошибка!
    PRINT 'Номер ошибки: ' + CAST(ERROR_NUMBER() AS nvarchar(10));
    PRINT 'Сообщение об ошибке: ' + ERROR_MESSAGE();
    PRINT 'Состояние ошибки: ' + CAST(ERROR_STATE() AS nvarchar(10));
    PRINT 'Серьезность ошибки: ' + CAST(ERROR_SEVERITY() AS nvarchar(10));
    PRINT 'Строка ошибки: ' + CAST(ERROR_LINE() AS nvarchar(10));
    PRINT 'Модуль ошибки: ' + COALESCE(ERROR_PROCEDURE(),'');
END CATCH;

НАЧАЛО ПОПЫТКИ
    DECLARE @unitprice INT=-20;
    IF @unitprice < 0
    BEGIN
    -- THROW допустимый диапазон начинается с 50000, уровень серьезности 16
        THROW 55555, 'Значение параметра @unitprice меньше нуля!', 1;
    END
    PRINT 'SOR'
END TRY
BEGIN CATCH
    PRINT 'Произошла ошибка!';
    PRINT 'Номер ошибки: ' + CAST(ERROR_NUMBER() AS nvarchar(10));
    PRINT 'Сообщение об ошибке: ' + ERROR_MESSAGE();
    PRINT 'Состояние ошибки: ' + CAST(ERROR_STATE() AS nvarchar(10));
    PRINT 'Серьезность ошибки: ' + CAST(ERROR_SEVERITY() AS nvarchar(10));
    PRINT 'Строка ошибки: ' + CAST(ERROR_LINE() AS nvarchar(10));
    PRINT 'Модуль ошибки: ' + COALESCE(ERROR_PROCEDURE(),'');
    SELECT CAST(@unitprice AS NVARCHAR(100)) + ' валус unitprice вызывает ошибку';
    RAISERROR ('%d значение цены единицы товара вызывает ошибку!', 15, 1, @unitprice);
END CATCH;

RAISERROR может интегрироваться в программу просмотра событий > журнал windows > приложения
генерировать уникальный код ошибки

НАЧАТЬ ПОПЫТКУ
    DECLARE @unitprice INT=-20;
    ЕСЛИ @unitprice < 0
    BEGIN
    -- RAISERROR
        DECLARE @myerrorcode UNIQUEIDENTIFIER=NEWID();
        DECLARE @myerrorstring varbinary(16)= CAST(@myerrorcode AS varbinary(16));
        RAISERROR ('Значение цены единицы товара %d вызывает ошибку! Ваш код ошибки: %x', 16, 1, @unitprice,@myerrorstring) WITH LOG;
    END
    PRINT 'SOR'
END TRY
BEGIN CATCH
    PRINT 'Произошла ошибка!
    PRINT 'Номер ошибки: ' + CAST(ERROR_NUMBER() AS nvarchar(10));
    PRINT 'Сообщение об ошибке: ' + ERROR_MESSAGE();
    PRINT 'Состояние ошибки: ' + CAST(ERROR_STATE() AS nvarchar(10));
    PRINT 'Серьезность ошибки: ' + CAST(ERROR_SEVERITY() AS nvarchar(10));
    PRINT 'Строка ошибки: ' + CAST(ERROR_LINE() AS nvarchar(10));
    PRINT 'Модуль ошибки: ' + COALESCE(ERROR_PROCEDURE(),'');
    SELECT CAST(@unitprice AS NVARCHAR(100)) + ' валус unitprice вызывает ошибку';
    ОТКАЗ;
END CATCH;

обработка ошибок при INSERT

USE testdb;
GO

DROP TABLE IF EXISTS dbo.military;
CREATE TABLE dbo.military(
    id INT IDENTITY(1,1) PRIMARY KEY
    , имя_солдата varchar(100) NOT NULL
    , soldier_bdate date CHECK (soldier_bdate > '19780101')
)

BEGIN TRY
    INSERT INTO dbo.military (soldier_name,soldier_bdate)
    VALUES ('John Ramobo', '19770101')
    -- RAISERROR
ЗАВЕРШИТЬ ПОПЫТКУ
НАЧАТЬ ОТЛОВ
    ЕСЛИ НОМЕР_ОШИБКИ()=547
    BEGIN
    DECLARE @myerrorcode UNIQUEIDENTIFIER=NEWID();
        DECLARE @myerrorstring varbinary(16)= CAST(@myerrorcode AS varbinary(16));
        RAISERROR ('Проверка нарушения ограничения. Ваш код ошибки: %x', 16, 1,@myerrorstring) WITH LOG;
    END
    PRINT 'Произошла ошибка!
    PRINT 'Номер ошибки: ' + CAST(ERROR_NUMBER() AS nvarchar(10));
    PRINT 'Сообщение об ошибке: ' + ERROR_MESSAGE();
    PRINT 'Состояние ошибки: ' + CAST(ERROR_STATE() AS nvarchar(10));
    PRINT 'Серьезность ошибки: ' + CAST(ERROR_SEVERITY() AS nvarchar(10));
    PRINT 'Строка ошибки: ' + CAST(ERROR_LINE() AS nvarchar(10));
    PRINT 'Модуль ошибки: ' + COALESCE(ERROR_PROCEDURE(),'');
END CATCH;
/*любая ошибка*/
НАЧАЛО ПОПЫТКИ
    INSERT INTO dbo.military (soldier_name,soldier_bdate)
    VALUES ('John Ramobo', '19770101')
    -- RAISERROR
ЗАВЕРШИТЬ ПОПЫТКУ
НАЧАЛО КАТЧА
    DECLARE @newerrormessage NVARCHAR(1000);
    SET @newerrormessage = 'Номер ошибки: ' + CAST(ERROR_NUMBER() AS nvarchar(10)) + CHAR(13) + 'Сообщение об ошибке: ' + ERROR_MESSAGE()+ CHAR(13) + 'Состояние ошибки: ' + CAST(ERROR_STATE() AS nvarchar(10))+ CHAR(13) + 'Серьезность ошибки: ' + CAST(ERROR_SEVERITY() AS nvarchar(10))+ CHAR(13) + 'Строка ошибки: ' + CAST(ERROR_LINE() AS nvarchar(10)) + CHAR(13) + 'Модуль ошибки: ' + COALESCE(ERROR_PROCEDURE(),'');
    DECLARE @myerrorcode UNIQUEIDENTIFIER=NEWID();
        DECLARE @myerrorstring varbinary(16)= CAST(@myerrorcode AS varbinary(16));
        RAISERROR ('%s Check constraint violation. Your error code: %x', 16, 1,@newerrormessage,@myerrorstring) WITH LOG;
END CATCH;

вложенный блок try-catch

НАЧНИТЕ ПОПЫТКУ
    ПРИНТ 10/0;
ЗАВЕРШИТЬ ПОПЫТКУ
НАЧАЛО КАТЧА
    НАЧАЛО ПОПЫТКИ
        INSERT INTO dbo.military (id,soldier_name,soldier_bdate)
        VALUES (1, 'John Ramobo', '19790101')
    END TRY
    BEGIN CATCH
        THROW 55555, 'Значение параметра @unitprice меньше нуля!', 2;
    END CATCH
END CATCH

ВИДЕО

- ссылка, значения пересчитываются при расчете, если только не индексированные представления

- Я скрываю представление от пользователей, не помещаю некоторые столбцы в представление и предоставляю к ним доступ
- на практике мы помещаем его в отдельную схему и предоставляем к нему доступ

ЕГЭ WideWorldImporters
GO
SELECT c.CustomerName, o.OrderID, SUM(ol.Quantity) as QuantitySum
FROM Sales.Customers as c
JOIN Sales.Orders as o ON o.CustomerID = c.CustomerID
JOIN Sales.OrderLines as ol ON ol.OrderID = o.OrderID
JOIN Warehouse.StockItems as si ON si.StockItemID = ol.StockItemID
GROUP BY c.CustomerName, o.OrderID;
GO
DROP VIEW IF EXISTS FirsView;
GO
CREATE VIEW FirstView
AS
SELECT c.CustomerName, o.OrderID, SUM(ol.Quantity) as QuantitySum
FROM Sales.Customers as c
JOIN Sales.Orders as o ON o.CustomerID = c.CustomerID
JOIN Sales.OrderLines as ol ON ol.OrderID = o.OrderID
JOIN Warehouse.StockItems as si ON si.StockItemID = ol.StockItemID
GROUP BY c.CustomerName, o.OrderID;
GO
SELECT * FROM FirstView;

С ПРИВЯЗКОЙ К СХЕМЕ, ШИФРОВАНИЕМ

SCHEMABINDING: мы не можем изменить его, чтобы сделать непригодным для использования, например, удалить столбец
ЭНКРИПЦИЯ: мы не можем видеть ваш код (создать для)

GO
CREATE VIEW SecondView
С ПРИВЯЗКОЙ К СХЕМЕ, ШИФРОВАНИЕМ
AS
SELECT c.CustomerName, o.OrderID, SUM(ol.Quantity) as QuantitySum
FROM Sales.Customers as c
JOIN Sales.Orders as o ON o.CustomerID = c.CustomerID
JOIN Sales.OrderLines as ol ON ol.OrderID = o.OrderID
JOIN Warehouse.StockItems as si ON si.StockItemID = ol.StockItemID
GROUP BY c.CustomerName, o.OrderID;
GO

ИЗМЕНИТЬ ВИД

- изменение представления без сброса - DROP VIEW IF EXISTS dbo.VarosokOrsz odok2;
-Используйте страницу VarosokOrszegok2Пока еще существует SCHEMABIDING

GO
ALTER VIEW dbo.VarosokOrsz odours2
AS
SELECT v.Nev AS VarosNev
o.Nev AS OrszagNev
FROM dbo.Varosok v
JOIN dbo.Orszagok o ON v.OrszagId=o.Id;
GO

ИНДЕКСИРОВАННЫЙ ВИД

сохраняется?

- представление должно быть детерминированным
- схематизация, также для пользовательских функций
- могут использовать только знаки

в каких случаях? каковы преимущества? индексированный вид
- находится на диске, поэтому он быстрее

проблема: быстрее выполнять запрос, если есть сложное объединение
если вы изменяете, вы также должны изменить представление

если вы пишете в таблице больше или столько же, сколько читаете, вы не применяете

GO
CREATE VIEW dbo.NemDet
AS
SELECT Nev, GetDate() AS RandomDate
FROM dbo.Varosok
GO

SELECT * FROM dbo.NemDet

CREATE UNIQUE CLUSTERED INDEX ix_Nemdet ON dbo.NemDet(Nev);
-- недетерминированный
GO
ALTER VIEW dbo.NemDet
С ПРИВЯЗКОЙ К СХЕМЕ
AS
SELECT Nev, GetDate() AS RandomDate
FROM dbo.Varosok
GO
CREATE UNIQUE CLUSTERED INDEX ix_Nemdet ON dbo.NemDet(Nev);

-- JOIN, может быть только для внутреннего

DROP VIEW IF EXISTS dbo.TobbTabla;

GO
CREATE VIEW dbo.TobbTabla
С ПРИВЯЗКОЙ К СХЕМЕ
AS
SELECT v.Nev as VarosNev, o.Nev AS OrszagNev
FROM dbo.Varosok v
JOIN dbo.Orszagok o ON v.OrszagId=o.Id

GO
SELECT * FROM dbo.TobbTabla;

CREATE UNIQUE CLUSTERED INDEX ix_Nemdet ON dbo.TobbTabla(VarosNev,OrszagNev);

-- CRUD - создание, чтение, обновление, удаление

-- ORDER BY - по умолчанию не возможен
-- ORDER BY - с TOP
GO
CREATE VIEW dbo.ViewOrderBy
С ПРИВЯЗКОЙ К СХЕМЕ
AS
SELECT TOP 1 v.Nev as VarosNev, o.Nev AS OrszagNev
FROM dbo.Varosok v
JOIN dbo.Orszagok o ON v.OrszagId=o.Id
ORDER BY o.Nev ASC;
GO

Хранимые процедуры

- Входы
- Выходы
- Может вызываться несколько раз

Преимущества хранимых процедур:

- передача параметров
- создает > план выполнения для хранимой процедуры - производительность - поиск параметров - использование другого параметра может быть очень медленным > WITH RECPOMPILE (новый план выполнения для каждого вызова)
- Вы можете сделать это отдельным делом
-https://www.brentozar.com/archive/2013/06/the-elephant-and-the-mouse-or-parameter-sniffing-in-sql-server/

- для каждого параметра тип должен быть

GO
ALTER VIEW dbo.VarosokOrsz odours2
AS
SELECT
v.Nev как VarosNev,
o.Nev как OrszagNev
FROM
dbo.Varosok as v
JOIN dbo.Orszagok as o ON v.OrszagId = o.Id
WHERE v.Nev LIKE 'B%'
GO

CREATE PROCEDURE dbo.TaroltEljarasVarosokOrszagok
AS
BEGIN
	SELECT
	v.Nev как VarosNev,
	o.Nev как OrszagNev
	FROM
	dbo.Varosok as v
	JOIN dbo.Orszagok as o ON v.OrszagId = o.Id
	WHERE v.Nev LIKE 'B%'
END

SELECT * FROM dbo.VarosokOrszagok2;

EXEC dbo.TaroltEljarasVarosokOrszagok;

CREATE TABLE #Eredmeny1
(
	VarosNev NVARCHAR(100),
	LocationNev NVARCHAR(100),
)

INSERT INTO #Eredmeny1
EXEC dbo.TaroltEljarasVarosokOrszagok

SELECT * FROM #Eredmeny1
GO
CREATE PROCEDURE dbo.VarosKereses(@SearchName NVARCHAR(100))
AS
BEGIN
	SELECT
	v.Nev как VarosNev,
	o.Nev как OrszagNev
	FROM
	dbo.Varosok as v
	JOIN dbo.Orszagok as o ON v.OrszagId = o.Id
	WHERE v.Nev LIKE @SearchName
END
GO

EXEC dbo.VarosKereses 'B%'
EXEC dbo.VarosKereses 'V%'

Что такое SQL-инъекция?

- Что набирает пользователь? Нормально ли это?
- "V" > OK
- "V" ; DROP TABLE dbo.students; SELECT * FROMdbo.Teachers WHERE something LIKE '" > ОЧЕНЬ НЕ ОК
- WHERE studentId = 123 AND v.Nev LIKE '"' " или WHERE v.Nev LIKE ''ни один из этих ' OR 1=1″ > ОЧЕНЬ НЕ ОК
-/**/OR//**/

- Каково решение?

EXEC dbo.VarosKereses ''none OR 1=1 "''

DECLARE @param NVARCHAR(100) = ''нет таких ИЛИ 1=1 "'';
EXEC dbo.VarosKereses @param;

GO
CREATE PROCEDURE dbo.VarosKereses2(@SearchName NVARCHAR(100))
AS
BEGIN
	SELECT * FROM dbo.Varosok WHERE Id=1 AND Nev LIKE @SearchName
END
GO

INSERT INTO dbo.Varosok(ID, NEV, OrszagId)
VALUES (100, 'none' OR ''''=''', 1);

SELECT * FROM dbo.Varosok WHERE Id =100;

Вам нужно будет указать параметр таблицы хранимой процедуры
как ЧИТАЕМЫЙ

DROP TYPE IF EXISTS mytable;
CREATE TYPE mytable AS table
(
    id int
);

GO
CREATE OR ALTER PROCEDURE dbo.GetSales(@myt mytable READONLY)
AS
SELECT id FROM @myt WHERE id > 1;
GO

DECLARE @t mytable;
INSERT INTO @t VALUES(1);
INSERT INTO @t VALUES(2);
INSERT INTO @t VALUES(3);

EXEC dbo.GetSales @t

Как мы можем получить возмещение?
OUTPUT - параметр! мы можем передать более одного выходного значения из нашей процедуры хранения

GO
CREATE OR ALTER PROCEDURE dbo.OutputTest (@param1 INT, @outparam int OUTPUT)
AS
BEGIN
SET @outparam=@param1*2;
END
GO

-- вернуть NULL
DECLARE @doubledValue int;
EXEC dbo.OutputTest 2,@doubledValue
SELECT @doubledValue;
GO

-- Это хорошо:
DECLARE @doubledValue int;
EXEC dbo.OutputTest 2, @doubledValue OUTPUT
SELECT @doubledValue;

-- Порядок параметров не может быть обратным
DECLARE @doubledValue int;
EXEC dbo.OutputTest @doubledValue OUTPUT,2;
SELECT @doubledValue;
GO

DECLARE @doubledValue int;
EXEC dbo.OutputTest 2,@doubledValue OUTPUT;
SELECT @doubledValue;

-- за исключением того, что haa задается именем
GO
DECLARE @doubledValue int;
EXEC dbo.OutputTest 2,@outparam = @doubledValue OUTPUT;

SELECT @doubledValue;

Возвращаемое значение - RETURN

GO
CREATE OR ALTER PROCEDURE dbo.ReturnTest(@varosId INT, @varosNev NVARCHAR(100) OUTPUT )
AS
ЕСЛИ @varosId < 0
	RETURN 1;
ELSE
	SET @varosNev = (SELECT Nev FROM dbo.Varosok WHERE Id = @varosId);
	RETURN 0;
GO

DECLARE @ret int;
DECLARE @varos NVARCHAR(100);
EXEC @ret = dbo.ReturnTest 2, @varos OUTPUT
SELECT @ret, @varos
GO
DECLARE @ret int;
DECLARE @varos NVARCHAR(100);
EXEC @ret = dbo.ReturnTest 1, @varos OUTPUT
SELECT @ret, @varos
GO
DECLARE @ret int;
DECLARE @varos NVARCHAR(100);
EXEC @ret = dbo.ReturnTest -3, @varos OUTPUT
SELECT @ret, @varos

SELECT * FROM dbo.Varosok

Выполняется до первого ВОЗВРАТА

- ранее, так что это больше не отменяет

GO
CREATE OR ALTER PROCEDURE dbo.ReturnTest (@varosId INT, @varosNev nvarchar(100) OUTPUT)
AS
BEGIN
если @varosID < 0
BEGIN
RETURN 1;
END

SELECT @varosNev= (SELECT Nev FROM dbo.Varosok WHERE Id = @varosID)
SET @varosNev = @varosNev + '!'
RETURN 0;
END
GO

DECLARE @ret int;
DECLARE @varos NVARCHAR(100);

EXEC @ret = dbo.ReturnTest 2, @varos OUTPUT
SELECT @ret,@varos;
- ранее возвращался @varos, так что это больше не перезаписывается
EXEC @ret = dbo.ReturnTest -3, @varos OUTPUT
SELECT @ret,@varos;
----------------MEGOLDÁS
GO
CREATE OR ALTER PROCEDURE dbo.ReturnTest (@varosId INT, @varosNev nvarchar(100) OUTPUT)
AS
SET @varosNev = NULL;
BEGIN
ЕСЛИ @varosID < 0
BEGIN
RETURN 1;
END

SELECT @varosNev= (SELECT Nev FROM dbo.Varosok WHERE Id = @varosID)
SET @varosNev = @varosNev + '!'
RETURN 0;
END
GO

DECLARE @ret int;
DECLARE @varos NVARCHAR(100);

EXEC @ret = dbo.ReturnTest 2, @varos OUTPUT
SELECT @ret,@varos;
-введенный ранее, поэтому он больше не перезаписывается
EXEC @ret = dbo.ReturnTest -3, @varos OUTPUT
SELECT @ret,@varos;
GO

ОШИБКА

GO
CREATE OR ALTER PROCEDURE dbo.PlaceOrder (@PaymentDate datetime2)
AS
IF @PaymentDate > GETDATE()
THROW 50001, 'Cannot Place an order in future',1;
GO
EXEC dbo.PlaceOrder '2024-01-01'
------------------------------------------------
GO
CREATE OR ALTER PROCEDURE dbo.Osztas(@v1 INT, @v2 INT, @hanyados INT OUTPUT)
AS
BEGIN TRY
SET @hanyados = @v1 / @v2
RETURN 0;
END TRY
НАЧАЛО ПЕРЕХВАТА
SET @hanyados = NULL;
RETURN 1;
КОНЕЦ КАТЧА
GO
DECLARE @ertek INT,@ret INT;
EXEC @ret=dbo.Osztas 10,2, @ertek OUTPUT;
SELECT @ret, @ertek;
GO
DECLARE @ertek INT,@ret INT;
EXEC @ret=dbo.Instal 10,0, @ertek OUTPUT;
SELECT @ret, @ertek;

значение DEFAULT

GO
CREATE OR ALTER PROCEDURE dbo.Osztas(@v1 INT, @v2 INT= 3, @hanyados INT OUTPUT)
AS
BEGIN TRY
SET @hanyados = @v1 / @v2
RETURN 0;
КОНЕЦ ИСПЫТАНИЯ
НАЧАЛО ПЕРЕХВАТА
SET @hanyados = NULL;
RETURN 1;
КОНЕЦ КАТЧА
GO
DECLARE @ertek INT,@ret INT;
EXEC @ret=dbo.Div 10, @hanyados = @ertek OUTPUT;
SELECT @ret, @ertek;
GO
DECLARE @ertek INT,@ret INT;
EXEC @ret=dbo.Instal 10,5, @ertek OUTPUT;
SELECT @ret, @ertek;

УСТАНОВИТЬ NOCOUNT НА

GO
CREATE OR ALTER PROCEDURE dbo.TaroltEljarasVarosokOrsz odors
AS
BEGIN
УСТАНОВИТЬ ЗНАЧЕНИЕ NOCOUNT НА
SELECT v.Nev AS VarosNev
o.Nev AS OrszagNev
FROM dbo.Varosok v
JOIN dbo.Orszagok o ON v.OrszagId=o.Id
WHERE v.Nev like 'B%';
END
GO
EXEC dbo.TaroltEljarasVarosokOrszagok

Сколько линий было изменено?

GO
CREATE OR ALTER PROCEDURE dbo.VarosFrissites (@searchParam nvarchar(10), @affectedRows INT OUTPUT)
AS
BEGIN
UPDATE dbo.Varosok
SET Nev =LOWER(Nev)
WHERE Nev like @searchParam;
SET @affectedRows=@@ROWCOUNT
END
GO

DECLARE @ar INT;
EXEC dbo.VarosFrissites 'B%', @ar OUTPUT;
PRINT @ar;

ОДС:

- всегда перезапускать
- не государственный, дает тип данных
- входной параметр
- хранимые процедуры не могут быть вызваны

Типы:
- функции системы
- Таблица-значение: не может быть параметризована в представлении
- Скалярные значения: могут быть в формате select
- Агрегат < внешние языки, например, .Net и функции C# могут быть импортированы
https://learn.microsoft.com/en-us/sql/t-sql/statements/create-aggregate-transact-sql?view=sql-server-ver15

TRY-CATCH - НЕ РАБОТАЕТ!!!!!!!!!!!!!!!!
но я вызываю процедуру из хранилища, и она находится там, тогда да.

DDL, вставка, обновление, удаление не работают
ИСПОЛЬЗОВАТЬ WideWorldImporters;
Перейти к
GO
CREATE OR ALTER FUNCTION Sales.function_date()
RETURNS datetime /* укажите тип данных возвращаемого значения */
AS
BEGIN
    /* возвращаем ваше значение */
    RETURN CURRENT_TIMESTAMP;
END;
GO
-- вызов функции
SELECT Sales.function_date();

общая_сумма_заказа

GO
CREATE FUNCTION Sales.total_sum_orderline(
    @ordelineID int
)
RETURNS decimal(20,3) --С СХЕМАБИНДИНГОМ - мы присоединяем его к структуре таблицы, что иногда помогает в производительности
AS
BEGIN
    RETURN (SELECT Quantity*UnitPrice FROM Sales.OrderLines Where OrderLineID = @ordelineID)
END;
GO
/*
    Использование UDF Scalar для возврата суммарного значения облагаемой налогом строки
    в предложении SELECT.
*/

SELECT Sales.total_sum_orderline(2);
SELECT Quantity*UnitPrice FROM Sales.OrderLines Where OrderLineID = 2;
/* для каждой строки total- будет медленно, подсчет по строкам и CPU будет высоким, нам не нравятся скалярные функции */
SELECT OrderLineID,Sales.total_sum_orderline(2)
FROM Sales.OrderLines;

Инкрустация скаляров UDF

- не выполняет UDF, а разбивает его на операции с таблицами > меньше процессора - ДОПОЛНИТЕЛЬНАЯ ФУНКЦИЯ
С 2019 года мы переупаковываем
Вы должны подчиниться:

Встраиваемая скалярная часть требований UDF
https://learn.microsoft.com/en-us/sql/relational-databases/user-defined-functions/scalar-udf-inlining?view=sql-server-ver15
Kiegészítés: https://support.microsoft.com/en-us/topic/kb4538581-fix-scalar-udf-inlining-issues-in-sql-server-2022-and-2019-f52d3759-a8b7-a107-1ab9-7fbee264dd5d

- например, возврат может быть только
- например, инкрустация Scalar UDF не работает с вычисляемым столбцом!

GO
CREATE OR ALTER FUNCTION dbo.total_sum_orderline(
    @ordelineID int
)
RETURNS decimal(20,3) WITH SCHEMABINDING -- мы присоединяем ее к структуре таблицы, что иногда помогает производительности
AS
BEGIN
    RETURN (SELECT Quantity*UnitPrice FROM dbo.testlines Where OrderLineID = @ordelineID)
END;
GO
SELECT dbo.total_sum_orderline(2);
SELECT Quantity*UnitPrice FROM dbo.testlines Where OrderLineID = 2;
/* вычисляемый столбец - мы теряем инлайнинг здесь - остаемся в пределах таблицы, если мы делаем это
мы также теряем его при проверке ограничений */
ALTER TABLE dbo.testlines
ADD totalsum AS dbo.total_sum_orderline(OrderID);

SELECT OrderLineID, totalsum FROM dbo.testlines
WHERE OrderLineID<200;
/*view-n - мы предпочитаем использовать его здесь */
GO
CREATE OR ALTER VIEW dbo.testview
С ПРИВЯЗКОЙ К СХЕМЕ
AS
SELECT OrderLIneID AS 'lineID
, dbo.total_sum_orderline(OrderLineID) as 'totalsum'
FROM dbo.testlines
GO
SELECT * FROM dbo.testview
WHERE LineID < 200;
/* если представление со схемотехникой содержит скалярную функцию, то скалярная функция также должна быть схемотехнической!!! */
/* проверьте ограничение - инлайнинг не работает */
ALTER TABLE dbo.testlines
ADD CONSTRAINT CK_test_1 CHECK (
(OrderLineID = 200);
UPDATE dbo.testlines
SET OrderLineID=1600
WHERE OrderLineID  500
    RETURN 500;

    RETURN @days;
END
GO
SELECT OrderID, OrderDate, dbo.datediffer(OrderID)
FROM dbo.testorders
WHERE OrderID  500
    BEGIN
        SET @days=500;
    END

    RETURN @days;
END
GO
SELECT OrderID, OrderDate, dbo.datediffer(OrderID)
FROM dbo.testorders
WHERE OrderID  500
    BEGIN
        SET @days=500;
    END

    RETURN @days;
END
GO
SELECT OrderID, OrderDate, dbo.datediffer(OrderID, GETDATE())
FROM dbo.testorders
WHERE OrderID  500
		BEGIN
			SET @days = 500;
		END
	RETURN @days
END
GO

SELECT OrderID,OrderDate,dbo.datediffer2(OrderDate,GETDATE()) AS 'days'
FROM dbo.testorders;

Значение DEFAULT для скалярного UDF

GO
CREATE OR ALTER FUNCTION dbo.defaultfn(@id int=6)
ВОЗВРАЩАЕТСЯ int
AS
BEGIN
    RETURN @id;
END
GO
SELECT dbo.defaultfn(2);
SELECT dbo.defaultfn(default);

Функции с табличными значениями:

- возвращаться всегда с таблицей
- нет JOIN

Типы:
- inline table-valued functions iTVF > 1 SELECT STATEMENT;
- multi statement - MSTVF(ужасный) - не используйте его, плохая оптимизация

ИСПОЛЬЗОВАНИЕ WideWorldImporters;
GO
/*
    Создание встроенного TVF для получения ColorId и ColorName
*/
CREATE FUNCTION Warehouse.GetColor(@colorid int)
ВОЗВРАЩАЕТ ТАБЛИЦУ
AS
RETURN
    (SELECT ColorID, ColorName
     FROM Warehouse.Colors
     WHERE ColorID = @colorid);
GO
/*
    Использование встроенного TVF в простом запросе SELECT в предложении FROM.
*/
SELECT ColorId, ColorName
FROM WareHouse.GetColor(36);

SELECT ColorId, ColorName
FROM WareHouse.GetColor(10);

Значение DEFAULT в TVF UDF

GO
CREATE OR ALTER FUNCTION Warehouse.GetColor(@colorid int=10)
ВОЗВРАЩАЕТСЯ В ТАБЛИЦУ
AS
RETURN
(SELECT ColorID, ColorName
FROM Warehouse.Colors
WHERE ColorID = @colorid);
GO
SELECT ColorId, ColorName
FROM WareHouse.GetColor(default);

APPLY - CROSS APPLY как внутреннее соединение

SELECT StockItemID,StockItemName,ColorName
FROM WareHouse.StockItems s
CROSS APPLY WareHouse.GetColor(s.ColorID) c;

APPLY - OUTER APPLY как LEFT OR RIGHT OUTER join

SELECT StockItemID,StockItemName,ColorName
FROM WareHouse.StockItems s
OUTER APPLY WareHouse.GetColor(s.ColorID) c;

нефункциональная часть
оператор стола:
cross aply вместо подзапроса (return )
определённый подзапрос

перекрестное применение вместо подзапроса (подзапрос возвращает 1 скалярное значение)
например
https://github.com/green-fox-academy/teaching-materials/blob/master/workshop/sql-server-subqueries-aggregations/correlated-quantity/correlated-quantity.md
Измените приведенный ниже запрос следующим образом:
- добавить новый столбец, который показывает максимальное количество для StockItemID
- добавить еще один столбец, показывающий разницу между максимальным
количество для идентификатора StockItemID и каждого количества заказа
| OrderID | StockItemID | Quantity | Max_StockItem_Qty | Max_Diff_Qty | Max_Diff_Qty

/* версия подзапроса: 0.1452 */
SELECT o.OrderID
				, o.StockItemID
				o.Quantity
				,( SELECT MAX(Quantity)
					FROM Sales.OrderLines
					WHERE StockItemID = 180
				 ) AS Max_StockItem_Qty
				,(SELECT maxq.qty - Quantity
						FROM ( SELECT MAX(Quantity) AS qty
										FROM Sales.OrderLines
										WHERE StockItemID = 180
								 ) AS maxq
				) AS Max_Diff_Qty
FROM Sales.OrderLines AS o
WHERE StockItemID = 180;
/* CROSS APPLY - версия оператора таблицы - стоимость CROSS APPLY 0.102256 */
SELECT OrderId,StockItemID,Quantity,Max_StockItem_Qty,Max_StockItem_Qty-Quantity AS Max_Diff_Qty
FROM Sales.OrderLines
	CROSS APPLY (SELECT MAX(Quantity) AS Max_StockItem_Qty FROM Sales.OrderLines
		WHERE StockItemID=180) temptable
WHERE StockItemID = 180;

TRIGGERS

- могу ли я запустить триггер? > НЕТ
- событие активирует триггер! "FIRE_TRIGGER"
- могу ли я дать параметр триггеру? -> НЕТ
- IF ... ELSE структура, пригодная для использования
- try ...catch структурированная обработка ошибок > YES
- создать простой триггер
Триггерные группы:
- Триггеры DML: INSERT, UPDATE, DELETE > МАСТЕРВЕРК
- DDL-триггер: ALTER, CREATE, DROP
- триггер входа в систему

Срабатывает LOGON: ОПАСНО! вы можете не войти в систему

USE testdb;
DROP TABLE IF EXISTS dbo.logonsactivity;
CREATE TABLE dbo.logonsactivity (
    id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
    login VARCHAR(50),
    servername VARCHAR(50),
    serverversion VARCHAR(350),
    servicename VARCHAR(50),
    logintime datetime2 DEFAULT GETDATE()
);
-- имя пользователя
SELECT SUSER_NAME();
-- имя сервера \ (экземпляр)
SELECT @@SERVERNAME;
-- версия сервера
SELECT @@VERSION;
-- имя сервиса (имя экземпляра без сервера)
SELECT @@SERVICENAME;
/*триггер*/
GO
CREATE OR ALTER TRIGGER tr_logtouser
НА ВСЕХ СЕРВЕРАХ ДЛЯ ВХОДА В СИСТЕМУ
AS
BEGIN
    INSERT INTO testdb.dbo.logonsactivity
    SELECT SUSER_NAME(),@@SERVERNAME,@@VERSION,@@SERVICENAME,CURRENT_TIMESTAMP;
END
GO
SELECT * FROM testdb.dbo.logonsactivity;
SELECT * FROM sys.server_triggers;
-- удалить триггер входа в систему
DROP TRIGGER tr_logtouser ON ALL SERVER;

Триггеры DDL

DROP TABLE IF EXISTS dbo.Invoices;
CREATE TABLE dbo.Invoices (
    id INT PRIMARY KEY,
    customer_id INT NOT NULL,
    total INT NOT NULL
);
INSERT INTO dbo.Invoices
VALUES
(3,5,200),(4,9,10000), (20,9,14000);
GO
CREATE OR ALTER TRIGGER invoiceprotector ON DATABASE FOR DROP_TABLE, ALTER_TABLE
КАК
BEGIN
PRINT 'вы не можете изменять или удалять никакие таблицы!'
ROLLBACK;
END
PRINT 'hello';
GO
SELECT * FROM sys.triggers;
ALTER TABLE dbo.Invoices
DROP COLUMN total;
DROP TRIGGER invoiceprotector ON DATABASE;

Триггеры DML:

- После срабатывания
- вместо спускового крючка

ПОСЛЕ УДАЛЕНИЯ

DROP TABLE IF EXISTS dbo.storno_invoices
CREATE TABLE dbo.storno_invoices (
    id INT, --PRIMARY KEY,
    customer_id INT NOT NULL,
    total INT NOT NULL,
    delete_time DATETIME2 NOT NULL DEFAULT CURRENT_TIMESTAMP
);
/* AFTER TRIGGER - создание триггера для перемещения удаленных счетов в таблицу strono_table
Триггер SQL SERVER работает на statement!!!!!
- Избегайте переменных! */
GO
CREATE TRIGGER dbo.TR_stroni_invoice ON dbo.Invoices AFTER DELETE
AS
УСТАНОВИТЬ NOCOUNT ON;
BEGIN
    INSERT INTO dbo.cancellation_invoices (id, customer_id, total)
    SELECT * FROM deleted;
END
GO
SELECT * FROM dbo.Invoices;
SELECT * FROM dbo.storno_invoices;

DELETE FROM dbo.Invoices
WHERE total > 1000;

SELECT * FROM dbo.Invoices;
SELECT * FROM dbo.storno_invoices;

создать триггер для запрета: изменение счета-фактуры

GO
CREATE OR ALTER TRIGGER dbo.tr_forbidden_modify_invoice
ON dbo.Invoices
ПОСЛЕ ОБНОВЛЕНИЯ
AS
УСТАНОВИТЬ NOCOUNT ON;
BEGIN
ROLLBACK;
END
GO
SELECT * FROM dbo.Invoices;

UPDATE dbo.Invoices
SET total=1000
WHERE id=3;

INSERT INTO dbo.Invoices
VALUES (25,9,2000),(92,20,20000),(100,19,21000);
UPDATE dbo.Invoices
SET total=1000
WHERE id=3;

где триггеры DML!

!!!!! TRUNCATE не может активироваться с помощью триггеров !!!!!!!!!!

ВЗАМЕН триггеров? > Триггеры DML

например, с самлаком большие уходят в другое место редко

создать триггер
где total >= 1100 > dbo.Invoces

где total dbo.smalltotalinvoices

DROP TABLE IF EXISTS dbo.smalltotalinvoices
  CREATE TABLE dbo.smalltotalinvoices (
    id INT PRIMARY KEY,
    customer_id INT NOT NULL,
    total INT NOT NULL
);
SELECT * FROM dbo.Invoices;
GO
CREATE OR ALTER TRIGGER dbo.tr_smalltotal
на dbo.Invoices
ВМЕСТО ВСТАВКИ
AS
УСТАНОВИТЬ NOCOUNT ON;
BEGIN
 INSERT INTO dbo.Invoices
 SELECT * FROM inserted
 WHERE total >=1100;

 INSERT INTO dbo.smalltototalinvoices
 SELECT * FROM inserted
 WHERE total < 1100;
END
GO
 INSERT INTO dbo.Invoices
 VALUES(3,200,50000);

  INSERT INTO dbo.Invoices
 VALUES(5,200,900);

SELECT * FROM dbo.Invoices;
SELECT * FROM dbo.smalltotalinvoices;

массовая вставка по умолчанию игнорирует триггер

массовая вставка dbo.Invoices
  FROM N'C:\Users\mrhen\Documents\READER\MatReview\WEEK-4\w1d1_smalltotal.csv'
  WITH (fieldterminator=';',rowterminator='0x0A'/* не '\n', потому что это было из Zoli, FIRSTROW=2, если есть заголовок*/)
TRUNCATE TABLE dbo.invoices;
TRUNCATE TABLE dbo.smalltotalinvoices;
/* DE FIRE_TRIGGERS */
массовая вставка dbo.Invoices
  FROM 'C:\Users\mrhen\Documents\READER\MatReview\WEEK-4\w1d1_smalltototal.csv'
  WITH (fieldterminator=';',rowterminator='0x0A', FIRE_TRIGGERS/* не '\n', потому что это было из Zoli, FIRSTROW=2, если есть заголовок*/)

SELECT * FROM dbo.Invoices;
SELECT * FROM dbo.smalltotalinvoices;
SELECT * FROM sys.triggers;
DROP TRIGGER dbo.TR_stroni_invoice, dbo.tr_forbidden_modify_invoice, dbo.tr_smalltototal;

Функция окна

- Оконные агрегатные функции (COUNT, SUM, MIN, MAX)
- Функции ранжирования (ROW_NUMBER, RANK, DENSE_RANK, NTILE)
- Функции смещения (LAG, LEAD, FIRST_VALUE, LAST_VALUE)
- Статистические функции (PERCENTILE_CONT, PERCENTILE_DISC, PERCENT_RANK, CUME_DIST)

OVER() == Оконная функция / SELECT /

наборы строк, учитывая набор строк окна,

ИСПОЛЬЗОВАНИЕ WideWorldImporters;
GO
/* stockitemid, sum_stock_quantities */
SELECT StockItemid, SUM(Quantity) AS sum_stock_quantities
FROM Sales.OrderLines
GROUP BY StockItemID;
GO
SELECT SUM(количество) AS sum_all_quantities
FROM Sales.OrderLines;
/* ФЕЛАДАТ
orderlineid stockitems sum_stock sum_all
1 1 20000 9million
2 1 20000
3 2 1000
*/
WITH myCTE AS (
    SELECT StockItemId, SUM(Quantity) AS sum_stock_quantities
    FROM Sales.OrderLines
    GROUP BY StockItemID
),
myCTE2 AS (
    SELECT SUM(количество) AS sum_all_quantities
    FROM Sales.OrderLines
)
SELECT sol.StockItemID,OrderLineID, myCTE.sum_stock_quantities,myCTE2.sum_all_quantitis, FORMAT(myCTE.sum_stock_quantities/(myCTE2.sum_all_quantitis*1.0),'P2') AS pct_all_quantity
FROM Sales.OrderLines sol
INNER JOIN myCTE ON myCTE.StockItemID=sol.StockItemID
CROSS JOIN myCTE2
ORDER BY sol.[StockItemID] ASC, OrderLineID ASC;
/*ПЕРЕЗАГРУЗКА CTE2 с помощью оконной функции */
SELECT SUM(Quantity) OVER ()
FROM Sales.OrderLines;
/*замена cte1 новой функцией*/
SELECT SUM(Количество) OVER (PARTITION BY StockItemID)
FROM Sales.OrderLines;
/* Версия функции Window */
SELECT sol.StockItemID,
OrderLineID,
SUM(Quantity) OVER (PARTITION BY StockItemID) AS sum_stock_quantitis,
SUM(Quantity) OVER () AS sum_all_quantities
, FORMAT(SUM(Quantity) OVER (PARTITION BY StockItemID)/(SUM(Quantity) OVER ()*1.0),'P2') AS pct_all_quantity
FROM Sales.OrderLines sol
ORDER BY sol.[StockItemID] ASC, OrderLineID ASC;

окна функция по группам по

SELECT sol.StockItemID,
SUM(Quantity) /*OVER (PARTITION BY StockItemID)*/ AS sum_stock_quantitis,
SUM(SUM(Quantity)) OVER () AS sum_all_quantities
, FORMAT(SUM(Quantity) /*OVER (PARTITION BY StockItemID)*//(SUM(SUM(Quantity)) OVER ()*1.0),'P2') AS pct_all_quantity
FROM Sales.OrderLines sol
GROUP BY StockItemID
ORDER BY sol.[StockItemID] ASC;

функции ранжирования

требование к синтаксису: ORDER BY внутри предложения OVER

ROW_NUMBER, RANK, DENSE_RANK, NTILE

SELECT OrderId, Customerid, OrderDate,
    ROW_NUMBER() OVER (ORDER BY OrderDate) AS ROW_NUMBER
    , Rank() OVER(ORDER BY OrderDate) AS rnk -- пропуски мертвой зоны и следующий ранг в строке
    , DENSE_RANK() OVER(ORDER BY OrderDate) AS dense_rnk -- пропуски тупиков в следующем ряду
    , NTILE (20) OVER(ORDER BY OrderDate) AS n_tile -- деление на 20 частей, возврат кучи
FROM Sales.Orders;

ранжирование по идентификатору заказа на идентификатор клиента

SELECT OrderId, CUstomerid, OrderDate,
    ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY OrderDate) AS ROW_NUMBER
    , Rank() OVER(PARTITION BY CustomerID ORDER BY OrderDate) AS rnk
    , DENSE_RANK() OVER(PARTITION BY CustomerID ORDER BY OrderDate) AS dense_rnk
    , NTILE (10) OVER(PARTITION BY CustomerID ORDER BY OrderDate) AS n_tile -- разделить на 20 частей, вернуть кучу
FROM Sales.Orders;

заказ в интернет-магазине какие товары находятся в заказе - общая сумма, номер_заказа

SELECT OrderId,
StockItemID,
Количество,
UnitPrice,
(UnitPrice*Количество) AS OrderLine_SUM,
ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY (SELECT NULL)) AS orderline_number,
SUM(UnitPrice*Quantity) OVER (PARTITION BY OrderID Order BY OrderLineID) as running_total
 FROM Sales.OrderLines;

 SELECT OrderId,
StockItemID,
Количество,
UnitPrice,
OrderLineID,
(UnitPrice*Количество) AS OrderLine_SUM,
ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY (SELECT NULL)) AS orderline_number,
SUM(UnitPrice*Quantity) OVER (PARTITION BY OrderID Order BY OrderLineID DESC) as running_total
 FROM Sales.OrderLines;

текущий итог

SELECT CityID, CityName, LatestRecordedPopulation AS population,
       (SELECT SUM(LatestRecordedPopulation)
        FROM Application.Cities c2
        WHERE c2.StateProvinceID = c1.StateProvinceID AND c2.CityID <= c1.CityID) AS run_total
FROM Application.Cities c1
WHERE StateProvinceID = 6
ORDER BY CityID;
/* версия функции для windows */
 SELECT CityID, CityName, LatestRecordedPopulation,
 SUM(LatestRecordedPopulation) OVER(ORDER BY CityID) as run_total
FROM Application.Cities
WHERE StateProvinceID = 6

функция смещения
(LAG, LEAD, FIRST_VALUE, LAST_VALUE)

/* идентификатор следующего города */
SELECT CityID, CityName, (SELECT MIN(CityID) FROM Application.Cities c2 WHERE c2.CityID > c1.CityID) AS nextcityid
FROM Application.Cities c1
ORDER by CityID ASC;
/* порядок функций windows */
SELECT CityID, CityName, LEAD(CityID) OVER(ORDER BY CityID) AS nextcityid
FROM Application.Cities
ORDER by CityID ASC;

SELECT CityID, CityName, LAG(CityID) OVER(ORDER BY CityID) AS nextcityid
FROM Application.Cities
ORDER BY CityID ASC;

ДИАГНОСТИЧЕСКИЕ ЗАПРОСЫ

Запросить таблицы из sys.tables

SELECT * FROM sys.tables;

Таблицы и их схемы - объединение представлений каталога sys.tables и sys.schemas

SELECT t.name AS [имя_таблицы], t.type_desc, s.name AS [имя_схемы]
FROM sys.tables t INNER JOIN sys.schemas s ON
t.schema_id = s.schema_id;

в какой сессии мы находимся

SELECT @@SPID;

получить информацию о типе данных столбцов для всех пользовательских таблиц

SELECT c.object_id, OBJECT_NAME(c.object_id) AS [имя_объекта],
s.name AS [schema], o.name AS [table_name], c.name AS [col_name],
type.name AS [col_type],
CASE c.is_nullable WHEN 1 THEN 'yes' ELSE 'no' END AS [nullable]
FROM sys.columns c INNER JOIN sys.objects o ON
c.object_id = o.object_id
INNER JOIN sys.tables t ON
t.object_id = o.object_id
INNER JOIN sys.types typ ON
c.user_type_id = typ.user_type_id
INNER JOIN sys.schemas s ON
s.schema_id = t.schema_id;

счетный индекс для каждой таблицы

SELECT S.name,COUNT(SYS.INDEXES.name) FROM SYS.INDEXES
ВНУТРЕННЕЕ СОЕДИНЕНИЕ SYS.OBJECTS S
ON S.OBJECT_ID=SYS.INDEXES.OBJECT_ID
WHERE SYS.INDEXES.OBJECT_ID IN (SELECT OBJECT_ID FROM SYS.OBJECTS WHERE name IN
    (SELECT name FROM SYS.TABLES WHERE NAME  'SYSDIAGRAMS'))
GROUP BY S.name;

индекс списка для каждой таблицы

SELECT S.name,SYS.INDEXES.name FROM SYS.INDEXES
ВНУТРЕННЕЕ СОЕДИНЕНИЕ SYS.OBJECTS S
ON S.OBJECT_ID=SYS.INDEXES.OBJECT_ID
WHERE SYS.INDEXES.OBJECT_ID IN (SELECT OBJECT_ID FROM SYS.OBJECTS WHERE name IN
    (SELECT NAME FROM SYS.TABLES WHERE name  'SYSDIAGRAMS'));

подсчитать ограничения (проверка+по умолчанию) в базе данных

SELECT (SELECT COUNT(*) FROM sys.check_constraints)+(SELECT COUNT(*) FROM sys.default_constraints);

Детерминированный?

SELECT COLUMNPROPERTY(OBJECT_ID('dbo.testlines'),'total_price','isDeterministic') AS Determ;

Select * FROM sys.computed_columns;

Поиск по имени столбца

ВЫБРАТЬ *
ИЗ INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME LIKE '%region%'

Поиск по имени таблицы

SELECT c.object_id, OBJECT_NAME(c.object_id) AS [имя_объекта], s.name AS [схема], o.name AS [имя_таблицы], c.name AS [имя_колонки], typ.name AS [тип_колонки]
, CASE c.is_nullable WHEN 1 THEN 'yes' ELSE 'no' END AS [nullable] FROM sys.columns c
INNER JOIN sys.objects o ON c.object_id = o.object_id
INNER JOIN sys.tables t ON t.object_id = o.object_id
INNER JOIN sys.types typ ON c.user_type_id = typ.user_type_id
INNER JOIN sys.schemas s ON s.schema_id = t.schema_id
WHERE c.name LIKE '%Postal%'

Поиск строки в базе данных

DECLARE @SearchStr nvarchar(100)
SET @SearchStr = 'Ограниченный запас

DECLARE @Results TABLE(ColumnName nvarchar(370), ColumnValue nvarchar(3630))

SET NOCOUNT ON

DECLARE @TableName nvarchar(256), @ColumnName nvarchar(128), @SearchStr2 nvarchar(110)
SET @TableName = ''
SET @SearchStr2 = QUOTENAME('%' + @SearchStr + '%','''')

WHILE @TableName IS NOT NULL
BEGIN
    SET @ColumnName = ''
    SET @TableName =
    (
        SELECT MIN(QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME))
        FROM INFORMATION_SCHEMA.TABLES
        WHERE TABLE_TYPE = 'BASE TABLE'
            AND QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME) > @TableName
            И OBJECTPROPERTY(
                    OBJECT_ID(
                        QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME)
                         ), 'IsMSShipped'
                           ) = 0
    )

    WHILE (@TableName IS NOT NULL) AND (@ColumnName IS NOT NULL)
    BEGIN
        SET @ColumnName =
        (
            SELECT MIN(QUOTENAME(COLUMN_NAME))
            FROM INFORMATION_SCHEMA.COLUMNS
            WHERE TABLE_SCHEMA = PARSENAME(@TableName, 2)
                AND TABLE_NAME = PARSENAME(@TableName, 1)
                AND DATA_TYPE IN ('char', 'varchar', 'nchar', 'nvarchar')
                AND QUOTENAME(COLUMN_NAME) > @ColumnName
        )

        IF @ColumnName IS NOT NULL
        BEGIN
            INSERT INTO @Results
            EXEC
            (
                'SELECT ''' + @TableName + '.'' + @ColumnName + '''', LEFT('' +
@ColumnName
+ ', 3630) FROM '
+
@TableName
+ ' (NOLOCK) ' +
                ' WHERE '
+
@ColumnName
+ ' LIKE '
+
@SearchStr2
                
            )
        END
    END
END

SELECT ColumnName, ColumnValue FROM @Results

Sys.messages & Изменение языка в сессии

8134 Сообщения об ошибках системы > диагностический запрос
SELECT * FROM sys.messages
WHERE message_id=8134;
/* изменение языка в сессии */
SET LANGUAGE hungarian;
PRINT 10/0;
SET LANGUAGE english;
PRINT 10/0;

VIEW диагностический запрос

SELECT * FROM sys.sql_modules WHERE [object_id] = OBJECT_ID('VarosokOrszagok2');
журналы базы данных по идентификатору базы данных
SELECT b.name,a.* FROM sys.databases b
CROSS APPLY sys.dm_db_log_info(b.database_id) a
WHERE b.name = 'WideWorldImporters';

перечислить все ТРИГГЕРА

SELECT * FROM sys.sql_modules WHERE [object_id] = OBJECT_ID('VarosokOrszagok2');

обнаружение заблокированных объектов

SELECT resource_type,request_status, request_mode, o.object_id, o.name, o.type_desc
FROM sys.dm_tran_locks l, sys.objects o
WHERE l.resource_associated_entity_id = o.object_id
    и resource_database_id = DB_ID();

определить уровень изоляции

SELECT CASE уровень_изоляции_транзакции
    WHEN 0 THEN 'Unspecified'
    WHEN 1 THEN 'ReadUncommitted'
    WHEN 2 THEN 'ReadCommitted'
    WHEN 3 THEN 'Repeatable
    WHEN 4 THEN 'Serializable
    WHEN 5 THEN 'Snapshot' END AS TRANSACTION_ISOLATION_LEVEL
FROM sys.dm_exec_sessions
-- используйте свой идентификатор сессии вместо 75
where session_id = 75;

Викторина по SQL

 

https://github.com/green-fox-academy/safely-syllabus/tree/master/materialreview/mssql

###### SQL средства #######

SELECT IN SELECT, WHERE IN, WHERE NOT IN

SELECT TOP (1000) [SAMLAHEAD_ID]
      ,[VEVO_ID]
      ,[CELTE]
      ,[TELJESITES]
      ,[FIZHATARIDO]
      ,[NAVATKULDVE]
      ,[STATUS]
  ОТ [CEGESADATOK].[samlazas].[samlahead].
  WHERE VEVO_ID Not IN (select vevo_id
						от samlazas.vevo)

UPDATE

ИСПОЛЬЗОВАТЬ [CEGESADATA]
GO

UPDATE [samlazas].
   УСТАНОВИТЬ [СТАТУС] = 1
      ,[LASTUPDATE] = (GETDATE())
 ГДЕ СТАТУС=0
GO

INSERT INTO

ИСПОЛЬЗОВАТЬ [CEGESADATA]
GO

INSERT INTO [samlazas].[vevo]
           ([VEVONEV], [ADOSZAM], [CIM])
     VALUES
           ('öTÖDIK Nyrt','3499789012-4-04','4000 Kisvarda, Negyedik utca. 5'),
		   ('Sixth Plc','3456789099-4-04','4000 Kisvarda, Negyedik utca. 6')
GO
/* MASIK */
ИСПОЛЬЗОВАТЬ [CEGESDATA]
GO

INSERT INTO [samlazas].[vevo]
           ([VEVONEV])
           ,[ADOSZAM]
           ,[CIM]
           ,[PHONE]
           ,[EMAIL]
           ,[STATUS])
     VALUES
           ('Hetedik Bt,
           '11223344-2-05',
           'None',
           '',
            ноль,
           0)
GO

УДАЛИТЬ

ИСПОЛЬЗОВАТЬ ЦЕГЕСДАТА
Перейти к

SELECT *
FROM szamlazas.szamlatetel
WHERE SZAMLAFEJ_ID Not IN (SELECT SZAMLAFEJ_ID
							FROM samlazas.samlahead)


SELECT HEAD_ID
FROM samlazas.samlahead

DELETE FROM samlazas.samlatetel
WHERE SZAMLAFEJ_ID Not IN (SELECT SZAMLAFEJ_ID
							FROM samlazas.samlahead)
GO

DISTINCT

SELECT DISTINCT v.*
FROM szamlazas.vevo v INNER JOIN szamlazas.szamlahead szf ON v.VEVO_ID=szf.VEVO_ID
WHERE v.STATUS=1 и szf.STATUS='normal'

КОНВЕРТ, АВГ

SELECT *
  ОТ [CEGESADATOK].[szamlazas].[ARU]

SELECT AVG(ASEGAR)
  ОТ [CEGESADATOK].[samlazas].[ARU]


SELECT *
  ОТ [CEGESADATOK].[samlazas].[ARU]
  где EGYSEGAR < (SELECT AVG(EGYSEGAR) as EGYSEGAR
					FROM [CEGESADATOK].[samlazas].[ARU])

SELECT ARU_ID, ARUNEV, MENNYEGYS, CONVERT(int,EGYSEGAR) as EGYSEGAR, CONVERT(int,AFAKULCS) as AFAKULCS, STATUS
  ОТ [CEGESADATOK].[szamlazas].[ARU]
  где EGYSEGAR < (SELECT AVG(EGYSEGAR) as EGYSEGAR
					FROM [CEGESADATOK].[samlazas].[ARU])

ИЗ ИСКУССТВА, ПРЕОБРАЗОВАТЬ

выберите getdate()
select FORMAT(getdate(), 'yyyy')
select FORMAT(getdate(), 'yyyy/MM')
select FORMAT(getdate(), 'yyyy/MM/dd')
select FORMAT(getdate()+1, 'yyyy/MM/dd')
select FORMAT(getdate()-2, 'yyyy/MM/dd')
select FORMAT(getdate(), 'yyyy/MM/dd HH:mm:ss.ms')

выберите '2022/3/15'
select CONVERT(datetime, '2022/3/25')

ИМЕЯ

/*Задача:
Найдите имена, адреса, налоговые номера и количество покупок клиентов, которые совершили не менее двух покупок
НЕПРАВИЛЬНО:
выберите v.VEVONEV
from szamlazas.vevo v inner join szamlazas.szamlafej szf on v.VEVO_ID=szf.VEVO_ID
где (select COUNT(szf.VEVO_ID)
							от szamlazas.szamlafej
							group by szf.VEVO_ID) > 1
ГРУППА В.ВЕВОНЕВА
*/

select COUNT(szf.VEVO_ID), szf.VEVO_ID
							от szamlazas.szamlafej szf
							группировать по szf.VEVO_ID

выберите v.VEVONEV, v.CIM, v.ADOSZAM,
	COUNT(szf.VEVO_ID) as VASARLASSZAM
from szamlazas.vevo v inner join szamlazas.szamlafej szf on v.VEVO_ID=szf.VEVO_ID
GROUP by v.VEVONEV, v.CIM, v.ADOSZAM
HAVING COUNT(szf.VEVO_ID) >=2
ЗАКАЗ ПО VASARLASSZAM

/*Задача:
Получите имя, адрес, налоговый номер и количество покупок, общую сумму брутто*/.
select v.VEVO_ID,v.VEVONEV,v.CIM, v.ADOSZAM,
	CONVERT(int,SUM(t.[BRUTTO ERTEK])) как SUM
from szamlazas.vevo v inner join szamlazas.szamlafej szf on v.VEVO_ID=szf.VEVO_ID
внутреннее соединение samlazas.tetelek t on szf.SZAMLAFEJ_ID=t.SZAMLAFEJ_ID
GROUP by v.VEVO_ID,v.VEVONEV,v.CIM,v.ADOSZAM
ORDER BY SZUMMABRUTTO desc


select v.VEVO_ID,v.VEVONEV,v.CIM, v.ADOSZAM,
	CONVERT(int,SUM(t.MENNYISEG*a.EGYSEGAR*(a.AFAKULCS/100+1))) as SUMABRUTTO
from szamlazas.vevo v inner join szamlazas.szamlafej szf on v.VEVO_ID=szf.VEVO_ID
внутреннее соединение samlazas.szamlatetel t on szf.SZAMLAFEJ_ID=t.SZAMLAFEJ_ID
внутреннее соединение samlazas.ARU a on t.ARU_ID=a.ARU_ID
WHERE v.CIM like '%Budapest%'
GROUP by v.VEVO_ID,v.VEVONEV,v.CIM,v.ADOSZAM
HAVING CONVERT(int,SUM(t.MENNYISEG*a.EGYSEGAR*(a.AFAKULCS/100+1))) > 121000
ORDER BY SUMABRUTTO desc

ПОСТОЯННЫЙ ФИКС

использовать ЦЕГЕСАДАТОК
перейти к

обновление szamlazas.szamlafej
	установить STATUS='error'
where SZAMLAFEJ_ID not in (select SZAMLAFEJ_ID from szamlazas.szamlatetel)

перейти к

СОК ПРИСОЕДИНИТЬСЯ, СОК ГДЕ

ИСПОЛЬЗОВАТЬ ЦЕГЕСДАТА
GO

/* Кто купил A7? */
выберите VEVONEV
От samlazas.vevo
WHERE VEVO_ID IN (select VEVO_ID
					FROM samlazas.samlahead
					WHERE SZAMLAFEJ_ID in (select SZAMLAFEJ_ID
											От samlazas.szamlatetel
											где ARU_ID IN (select ARU_ID
																От samlazas.ARU
																где ARUNEV='A7')))


select v.*
FROM szamlazas.vevo v
INNER JOIN samlazas.samlahead f ON v.VEVO_ID=f.VEVO_ID
	INNER JOIN samlazas.szamlatetel t ON f.SZAMLAFEJ_ID=t.SZAMLAFEJ_ID
		INNER JOIN samlazas.ARU a ON t.ARU_ID=a.ARU_ID
			WHERE a.ARUNEV='A7'
ORDER BY VEVO_ID

выберите *
From szamlazas.vevo v INNER JOIN szamlazas.szamlazfej sz ON v.VEVO_ID=sz.VEVO_ID
INNER JOIN samlazas.tetelek t ON sz.SZAMLAFEJ_ID=t.SZAMLAFEJ_ID
где t.ARUNEV='A7'




выберите *
FROM samlazas.samlahead
WHERE SZAMLAFEJ_ID in (select SZAMLAFEJ_ID
						От samlazas.szamlatetel
						где ARU_ID IN (select ARU_ID
											От samlazas.ARU
											где ARUNEV='A7'))

выберите *
От szamlazas.szamlatetel
где ARU_ID IN (select ARU_ID
					От samlazas.ARU
					где ARUNEV='A7')

/*
выберите *
From samlazas.tetelek t INNER JOIN samlazas.samlafej sz ON t.SZAMLAFEJ_ID=sz.SZAMLAFEJ_ID
где ARUNEV='A7'

выберите *
From samlazas.vevo v INNER JOIN (samlazas.tetelek t INNER JOIN samlazas.samlahead sz ON t.SZAMLAFEJ_ID=sz.SZAMLAFEJ_ID) ON
где ARUNEV='A7'
*/

ЗАПРОСИТЬ ВИД НА

ИСПОЛЬЗОВАТЬ ЦЕГЕСДАТА
GO

SELECT t.*, a.ARUNEV, a.MENNYEGYS, a.EGYSEGAR, a.EGYSEGAR*t.MENNYISEG as 'Netto ertek', a.EGYSEGAR*t.MENNYISEG*(a.AFAKULCS/100) as 'AFA ERTEK'
, (a.EGYSEGAR*t.MENNYISEG*(a.AFAKULCS/100))+(a.EGYSEGAR*t.MENNYISEG) как 'GROSS ERTEK'
FROM szamlazas.szamlatetel t INNER JOIN szamlazas.ARU a ON t.ARU_ID=a.ARU_ID
/*WHERE (t.ARU_ID=a.ARU_ID)*/

SELECT *
FROM samlazas.tetelek

СОЗДАТЬ ВИД

создать представление samlazas.bad_samla_head
в качестве
выберите *
из samlazas.samlahead
where VEVO_ID not in (select vevo_id from szamlazas.vevo)

Создайте схему

ИСПОЛЬЗОВАТЬ [CEGESADATA]
GO

/****** Объект: schema [samlazas] Дата сценария: 2022. 10/25 0:11:57 ******/
CREATE SCHEMA [samlazas]
GO

Создание пользователя, входа в систему, разрешений, ролей

USE [master]
GO
CREATE LOGIN [test1] WITH PASSWORD=N'Password123', DEFAULT_DATABASE=[CEGESADATOK], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF
GO
ИСПОЛЬЗОВАТЬ [CEGESADATOK]
GO
CREATE USER [test1] FOR LOGIN [test1]
GO
ИСПОЛЬЗОВАТЬ [CEGESDATA]
GO
ALTER ROLE [db_backupoperator] ADD MEMBER [test1]
GO
ИСПОЛЬЗОВАТЬ [CEGESDATA]
GO
ALTER ROLE [db_datareader] ADD MEMBER [test1]
GO
ИСПОЛЬЗОВАТЬ [CEGESADATA]
GO
ALTER ROLE [db_datawriter] ADD MEMBER [test1]
GO
ИСПОЛЬЗОВАТЬ [CEGESDATA]
GO
ALTER ROLE [db_ddladmin] ADD MEMBER [test1]
GO
ИСПОЛЬЗОВАТЬ [CEGESDATA]
GO
ALTER ROLE [db_securityadmin] ADD MEMBER [test1]
GO

Удаление таблицы, усечение таблицы

/****** Сценарий для команды SelectTopNRows из SSMS ******/
SELECT TOP (1000) [SAMLAHEAD_ID]
      ,[VEVO_ID]
      ,[KELTE]
      ,[TELJESITES]
      ,[FIZHATARIDO]
      ,[NAVATKULDVE]
      ,[STATUS]
  ОТ [CEGESADATOK].[samlazas].[samlahead].

  усечь таблицу szamlazas.szamlafej

  массовая вставка samlazas.samlahead
  FROM N'E:\szamlafej.table.csv'
  with (fieldterminator=';',rowterminator='\n')

######3 экзамен SQL ########

Создание таблиц (создать таблицу)

CREATE TABLE apprentices(
	id int,
	имя varchar(50),
	возраст инт,
	страна varchar(50),
	cohort_id int)

CREATE TABLE cohorts(
	id int,
	имя varchar(50),
	дата начала_действия,
	тип varchar(50))

Импорт таблиц (массовая вставка)

/****** Сценарий для команды SelectTopNRows из SSMS ******/
  ученики с объемными вставками
  FROM N'E:\apprentices.table.csv'
  WITH (fieldterminator=';',rowterminator='\n'/*, FIRSTROW=2 ha van fejlec*/)

  массовая вставка когорт
  FROM N'E:\cohorts.table.csv'
  WITH (fieldterminator=';',rowterminator='\n')

Перечислите имя (name) и дату начала (started_at) всех когорт (таблица cohorts).
Filename: cohorts.sql

SELECT имя, started_at
 ОТ когорт

Перечислите имя и страну происхождения всех студентов (таблица учеников)
Filename: apprentices.sql

SELECT имя, страна
ОТ учеников

Укажите имя и возраст студентов (таблица учеников), которые моложе 21 года
Filename: apprentices-younger-21.sql

SELECT имя, возраст
ОТ учеников
ГДЕ возраст < 21

Перечислите имена и фамилии студентов (таблица учеников), которые не являются жителями Венгрии (страна)
Filename: apprentices-not-from-hu.sql

SELECT id, name
ОТ учеников
Страна, которая не похожа на "Венгрию

Выпишите, сколько студентов (таблицы учеников) в базе данных в возрасте 20 или более и 30 или менее лет
Filename: apprentices-between-20-and-30.sql

SELECT count(id)
ОТ учеников
WHERE возраст >= 20 и возраст <= 30

Выпишите, сколько таблиц когорт в базе данных имеют тип "неполный рабочий день" (тип)
Filename: cohorts-part-time.sql

SELECT count(id)
ОТ когорт
WHERE type like 'part-time'

Перечислите имена учеников (как имя_ученика) и имена когорт (как имя_группы) студентов, которые начали обучение в когортах 2018 года.
Filename: apprentices-in-2018.sql

SELECT a.name as apprentice_name, c.name as cohort_name
FROM ученики a INNER JOIN когорты c ON a.cohort_id=c.id
WHERE FORMAT(started_at, 'yyyy')=2018

Выпишите, сколько классов в базе данных, для которых нет учеников в базе данных
Filename: cohorts-without-apprentice.sql

SELECT count(id)
ОТ когорт
WHERE id NOT IN (SELECT cohort_id FROM apprentices)

Перечислите имена студентов и дату начала обучения в алфавитном порядке по фамилии студента
Filename: apprentices-start-date-ordered.sql

SELECT a.name, c.started_at
FROM ученики a INNER JOIN когорты c ON a.cohort_id=c.id
ORDER BY a.name

Перечислить названия классов и количество учеников (как numberOfApprentices) в данном классе, в порядке убывания количества учеников. Включите когорту, в которой в настоящее время 0 студентов.
Filename: cohort-sizes.sql

SELECT c.name, count(a.id) as numberOfApprentices
FROM когорты c LEFT JOIN ученики a ON a.cohort_id=c.id
GROUP BY c.name
ORDER BY count(a.id) DESC

Задачи проекта SQL admin ######### ####

1) На сервер добавляется диск, который еще не используется
(20 ГБ). Добавьте S: в качестве диска и имени тома:
SQLDATA должна быть.

2) На диске S: создайте каталог SQLDATA, а в нем
еще два подкаталога с именами DB и LOG.

3) Войдите в SQL-сервер (экземпляр GREENFOX) (Этот шаг
вы не обязаны сохранять его! :-)

4) Восстановите файл C:\install\MSSQL\AdventureWorks2016.bak
базы данных на экземпляр сервера GREENFOX, чтобы база данных
файлы находятся в S:\SQLDATA\DB, а файлы журналов - в S:\SQLDATA\
Библиотека LOG.

 
USE [master]
RESTORE DATABASE [AdventureWorks2016] FROM DISK = N'C:\install\MSSQL\AdventureWorks2016.bak' WITH FILE = 1, MOVE N'AdventureWorks2016_Data' TO N'S:\SQLDATADB\AdventureWorks2016_Data.mdf', MOVE N'AdventureWorks2016_Log' TO N'S:\SQLDATA\LOG\AdventureWorks2016_Log.ldf', NOUNLOAD, STATS = 5

GO

5) Создайте аутентифицированную учетную запись interface_svc MS SQL,
с минимальными (публичными) правами! Ваш пароль должен быть INTERface.

USE [master]
GO
CREATE LOGIN [interface_svc] WITH PASSWORD=N'INTERFACE', DEFAULT_DATABASE=[master], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF
GO

6) Создайте базу данных под названием INTERFACE со следующими настройками:
- Имя базы данных: INTERFACE
- Владелец: interface_svc
- Модель восстановления: FULL
- Начальный файл базы данных:
(a)размер: 32 МБайт
(b)местоположение: S:\SQLDATA\DB
- Файл журнала запуска:
(a) размер: 16 МБайт
(b)местоположение: S:\SQLDATA\LOG

СОЗДАТЬ БАЗУ ДАННЫХ [ИНТЕРФЕЙС]
 СДЕРЖИВАНИЕ = НЕТ
 НА ПЕРВИЧНОЙ ОСНОВЕ
( NAME = N'INTERFACE', FILENAME = N'S:\SQLDATA\DB\INTERFACE.mdf' , SIZE = 32768KB , FILEGROWTH = 65536KB )
 ВОЙТИ В СИСТЕМУ
( NAME = N'INTERFACE_log' , FILENAME = N'S:\SQLDATA\LOG\INTERFACE_log.ldf' , SIZE = 16384KB , FILEGROWTH = 65536KB )
GO

ИСПОЛЬЗОВАТЬ [ИНТЕРФЕЙС]
GO
ALTER AUTHORIZATION ON DATABASE::[INTERFACE] TO [interface_svc]
/*
exec sp_changedbowner 'interface_svc'
GO
*/

ALTER DATABASE [INTERFACE] SET COMPATIBILITY_LEVEL = 150
GO
ALTER DATABASE [INTERFACE] SET ANSI_NULL_DEFAULT OFF
GO
ALTER DATABASE [INTERFACE] SET ANSI_NULLS OFF
GO
ALTER DATABASE [INTERFACE] SET ANSI_PADDING OFF
GO
ALTER DATABASE [INTERFACE] SET ANSI_WARNINGS OFF
GO
ALTER DATABASE [INTERFACE] SET ARITHABORT OFF
GO
ALTER DATABASE [INTERFACE] SET AUTO_CLOSE OFF
GO
ALTER DATABASE [INTERFACE] SET AUTO_SHRINK OFF
GO
ALTER DATABASE [INTERFACE] SET AUTO_CREATE_STATISTICS ON(INCREMENTAL = OFF)
GO
ALTER DATABASE [INTERFACE] SET AUTO_UPDATE_STATISTICS ON
GO
ALTER DATABASE [INTERFACE] SET CURSOR_CLOSE_ON_COMMIT OFF
GO
ALTER DATABASE [INTERFACE] SET CURSOR_DEFAULT GLOBAL
GO
ALTER DATABASE [INTERFACE] SET CONCAT_NULL_YIELDS_NULL OFF
GO
ALTER DATABASE [INTERFACE] SET NUMERIC_ROUNDABORT OFF
GO
ALTER DATABASE [INTERFACE] SET QUOTED_IDENTIFIER OFF
GO
ALTER DATABASE [INTERFACE] SET RECURSIVE_TRIGGERS OFF
GO
ALTER DATABASE [INTERFACE] SET DISABLE_BROKER
GO
ALTER DATABASE [INTERFACE] SET AUTO_UPDATE_STATISTICS_ASYNC OFF
GO
ALTER DATABASE [INTERFACE] SET DATE_CORRELATION_OPTIMIZATION OFF
GO
ALTER DATABASE [INTERFACE] SET PARAMETERIZATION SIMPLE
GO
ALTER DATABASE [INTERFACE] SET READ_COMMITTED_SNAPSHOT OFF
GO
ALTER DATABASE [INTERFACE] SET READ_WRITE
GO
ALTER DATABASE [INTERFACE] SET RECOVERY FULL
GO
ALTER DATABASE [INTERFACE] SET MULTI_USER
GO
ALTER DATABASE [INTERFACE] SET PAGE_VERIFY CHECKSUM
GO
ALTER DATABASE [INTERFACE] SET TARGET_RECOVERY_TIME = 60 SECONDS
GO
ALTER DATABASE [INTERFACE] SET DELAYED_DURABILITY = DISABLED
GO
ИСПОЛЬЗОВАТЬ [ИНТЕРФЕЙС]
GO
ALTER DATABASE SCOPED CONFIGURATION SET LEGACY_CARDINALITY_ESTIMATION = Off;
GO
ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET LEGACY_CARDINALITY_ESTIMATION = Primary;
GO
ALTER DATABASE SCOPED CONFIGURATION SET MAXDOP = 0;
GO
ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET MAXDOP = PRIMARY;
GO
ALTER DATABASE SCOPED CONFIGURATION SET PARAMETER_SNIFFING = On;
GO
ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET PARAMETER_SNIFFING = Primary;
GO
ALTER DATABASE SCOPED CONFIGURATION SET QUERY_OPTIMIZER_HOTFIXES = Off;
GO
ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET QUERY_OPTIMIZER_HOTFIXES = Primary;
GO
ИСПОЛЬЗОВАТЬ [ИНТЕРФЕЙС]
GO
IF NOT EXISTS (SELECT name FROM sys.filegroups WHERE is_default=1 AND name = N'PRIMARY') ALTER DATABASE [INTERFACE] MODIFY FILEGROUP [PRIMARY] DEFAULT
GO

7) Предоставьте учетной записи interface_svc только разрешение на запрос с
База данных AdventureWorks2016 в схеме HumanResources
vВидовой экран сотрудника (VIEW)!

использовать [AdventureWorks2016]
GO
CREATE USER [interface_svc] FOR LOGIN [interface_svc]
GO
GRANT SELECT ON [HumanResources].[vEmployee] TO [interface_svc]
GO

8) Создайте схему AWDB в базе данных INTERFACE. Сценарий
"Создать в формате "

ИСПОЛЬЗОВАТЬ [ИНТЕРФЕЙС]
GO

/****** Объект: schema [AWDB] Script Date: 12/9/2022 8:21:47 AM ******/
СОЗДАТЬ СХЕМУ [AWDB]
GO

9) В схеме AWDB создайте синоним под названием AW_Employee,
которая находится в схеме HumanResources базы данных AdventureWorks2016
указывает на область просмотра vEmployee!

ИСПОЛЬЗОВАТЬ [ИНТЕРФЕЙС]
GO

/****** Объект: синоним [AWDB].[AW_Employee] Дата сценария: 12/6/2022 1:59:44 PM ******/
CREATE SYNONYM [AWDB].[AW_Employee] FOR [AdventureWorks2016].[HumanResources].[vEmployee]
GO

10) Создайте запрос, который будет отправлен в базу данных INTERFACE с помощью AWDB
все строки и столбцы синонима AW_Employee в схеме
.................

ИСПОЛЬЗОВАТЬ [ИНТЕРФЕЙС]
GO
SELECT *
FROM AWDB.AW_Employee

11) Создайте ПОЛНУЮ резервную копию базы данных INTERFACE, используя следующее
согласно:
a) Имя файла резервной копии: S:\SQLdata\INTERFACE.bak
b) Сохранение перезаписывает одноименный медиа-набор!

BACKUP DATABASE [INTERFACE] TO DISK = N'S:\SQLdata\INTERFACE.bak' WITH NOFORMAT, INIT, NAME = N'S:\SQLdata\INTERFACE.bak', SKIP, NOREWIND, NOUNLOAD, STATS = 10
GO

1) Узнайте версию SQL-сервера и выпуск SQL-сервера!

ВЫБЕРИТЕ @@VERSION

2) Сделайте простой "Hello World!". запрос!

SELECT 'Hello World!'

3) Создайте SQL-запрос, который вычисляет квадратный корень из 30!

ВЫБЕРИТЕ SQRT(30)

4) Создайте SQL-запрос, который возвращает сегодняшнюю дату!

SELECT CAST(GETDATE() as date)

5) Создайте SQL-запрос, который подсчитывает, сколько дней прошло
С 15 марта 1989 года!

SELECT DATEDIFF(day,'1989-03-15', GETDATE());

6) Создайте SQL-запрос, который возвращает загруженные ранее данные
Первые 20 элементов таблицы Person.Address в базе данных AdventureWorks
(все поля)!

/****** Сценарий для команды SelectTopNRows из SSMS ******/
ВЫБЕРИТЕ TOP (20) *
  FROM [AdventureWorks2016].[Person].[Address]

7) Создайте SQL-запрос, который возвращает загруженные ранее данные
Первые 20 элементов таблицы Person.Person в базе данных AdventureWorks
(все поля), но где поле PersonType - 'EM'!

/****** Сценарий для команды SelectTopNRows из SSMS ******/
ВЫБЕРИТЕ TOP (20) *
  FROM [AdventureWorks2016].[Person].[Person].[Person]
  WHERE [AdventureWorks2016].[Person].[Person].PersonType='EM'

8) Создайте SQL-запрос, который возвращает загруженные ранее данные
первые 20 элементов таблицы Person.Person в базе данных AdventureWorks, но
чтобы в поле PersonType было 'EM' и только PersonType,
Поля FirstName, LastName должны быть в результате запроса!

/****** Сценарий для команды SelectTopNRows из SSMS ******/
SELECT TOP (20) PersonType, FirstName, LastName
  FROM [AdventureWorks2016].[Person].[Person].[Person]
  WHERE [AdventureWorks2016].[Person].[Person].PersonType='EM'

9) Создайте SQL-запрос, который возвращает загруженные ранее данные
первые 20 элементов таблицы Person.Person в базе данных AdventureWorks, но
чтобы в поле PersonType было 'EM' и только PersonType,
Поля FirstName, LastName должны быть в результате запроса, но поля
Имя и фамилия должны отображаться в столбце с одним пробелом
разделенные символом!

/****** Сценарий для команды SelectTopNRows из SSMS ******/
SELECT TOP (20) PersonType, FirstName + ' ' + LastName
  FROM [AdventureWorks2016].[Person].[Person].[Person]
  WHERE PersonType='EM'

10)Создайте SQL-запрос, который возвращает ранее загруженные
записи в таблице Person.Person базы данных AdventureWorks
(все поля), где поле PersonType равно 'EM', а FirstName
поле должно показывать только тех, у кого есть имя "Джон"!

/****** Сценарий для команды SelectTopNRows из SSMS ******/
SELECT *
  FROM [AdventureWorks2016].[Person].[Person].[Person]
  WHERE PersonType='EM' AND FirstName='John'

11)Создайте SQL-запрос, который возвращает ранее загруженные данные
записи в таблице Person.Person базы данных AdventureWorks
(все поля), где поле PersonType равно 'EM', а FirstName
поле должно отображать только тех, кто имеет имя 'John', где
Значение поля LastName заканчивается символом 'e'!

/****** Сценарий для команды SelectTopNRows из SSMS ******/
SELECT *
  FROM [AdventureWorks2016].[Person].[Person].[Person]
  WHERE PersonType='EM' AND FirstName='John' AND RIGHT(LastName,1)='e'

12)Создайте SQL-запрос, который возвращает ранее загруженные
записи в таблице Person.Person базы данных AdventureWorks
(все поля), где поле PersonType равно 'EM', а FirstName
поле должно отображать только тех, кто имеет имя 'John', где
Значения поля LastName содержат либо 'Chen', либо 'Kane'!

/****** Сценарий для команды SelectTopNRows из SSMS ******/
SELECT *
  FROM [AdventureWorks2016].[Person].[Person].[Person]
  WHERE PersonType='EM' AND FirstName='John' AND (LastName='Chen' OR LastName='Kane')

13) Посчитайте, сколько различных имен (FirstName) есть в
в таблице Person.Person ранее загруженной базы данных AdventureWorks!

/****** Сценарий для команды SelectTopNRows из SSMS ******/
SELECT COUNT(DISTINCT FirstName)
  FROM [AdventureWorks2016].[Person].[Person].[Person]

14) Подсчитайте количество записей с первым именем 'Ken' (FirstName) в списке
в таблице Person.Person ранее загруженной базы данных AdventureWorks!

/****** Сценарий для команды SelectTopNRows из SSMS ******/
SELECT COUNT(FirstName)
  FROM [AdventureWorks2016].[Person].[Person].[Person]
  WHERE FirstName='Ken'

15) Подсчитайте, сколько записей с одинаковым именем (FirstName и LastName)
это ранее загруженная база данных AdventureWorks Person.Person
на вашей доске! Расположите результаты так, чтобы наиболее используемые
пары имен в верхней части списка и только 20 лучших результатов
будьте включены!

/****** Сценарий для команды SelectTopNRows из SSMS ******/
SELECT TOP (20) FirstName, LastName, COUNT(1)
  FROM [AdventureWorks2016].[Person].[Person].[Person]
  GROUP BY FirstName, LastName
  ORDER BY COUNT(1) DESC

16) Подсчитайте, сколько записей с одинаковым именем (FirstName и LastName)
это ранее загруженная база данных AdventureWorks Person.Person
на вашей доске! Расположите результаты так, чтобы было только 4 и более 4
используемые для пары имен, должны быть указаны!

SELECT FirstName, LastName, COUNT(1)
  FROM [AdventureWorks2016].[Person].[Person].[Person]
  GROUP BY FirstName, LastName
  ЕСЛИ COUNT(1) >= 4
  ORDER BY COUNT(1) DESC

17)Создайте SQL-запрос, который возвращает ранее загруженный
База данных AdventureWorks Person.Person и Person.EmailAddress
таблицы со следующими полями: Person.Person.FirstName,
Person.Person.LastName, Person.EmailAddress.EmailAddress таким образом, чтобы
для связи таблиц, поле BusinessEntityID используется для обеих таблиц.
из совета директоров! Отобразите только первые 50 строк результата!

SELECT TOP (50) p.FirstName, p.LastName, e.EmailAddress
  FROM [AdventureWorks2016].[Person].[Person] p INNER JOIN [AdventureWorks2016].[Person].[EmailAddress] e on p.BusinessEntityID=e.BusinessEntityID

18)На основе предыдущего запроса создайте решение, в котором адрес thee-mail
(EmailAddress) только часть имени (до появления части @)!

SELECT TOP (50) p.FirstName, p.LastName, LEFT(e.EmailAddress,CHARINDEX('@',e.EmailAddress)-1)
  FROM [AdventureWorks2016].[Person].[Person] p INNER JOIN [AdventureWorks2016].[Person].[EmailAddress] e on p.BusinessEntityID=e.BusinessEntityID

19)Или запрос, в котором только доменная часть (после @)
заметно!

SELECT TOP (50) p.FirstName, p.LastName, RIGHT(e.EmailAddress,LEN(e.EmailAddress)- CHARINDEX('@',e.EmailAddress))
  FROM [AdventureWorks2016].[Person].[Person] p INNER JOIN [AdventureWorks2016].[Person].[EmailAddress] e on p.BusinessEntityID=e.BusinessEntityID

20) (ВРЕМЕННОЕ ЗАДАНИЕ) Создайте запрос, который возвращает
Sales.SalesPerson из поля BusinessEntityID и "minsales"
это вычисляемый столбец. Значение minsales должно быть, построчно, SalesYTD
поле, если SalesYTD > 1000000, иначе NULL
добавлять ценность.

/****** Сценарий для команды SelectTopNRows из SSMS ******/
SELECT [BusinessEntityID], CASE WHEN SalesYTD > 1000000 THEN SalesYTD ELSE NULL END as minsales
  FROM [AdventureWorks2016].[Sales].[SalesPerson]

21) (ОЧЕНЬ ВАЖНОЕ ЗАДАНИЕ) Создайте запрос, который возвращает значение
Sales.SalesPerson из поля BusinessEntityID и "salesinfo"
это вычисляемый столбец. Значение salesinfo должно быть установлено для каждой строки в SalesYTD
поле, если SalesYTD >= 2000000, Если
SalesYTD<2000000, но больше или равно 1000000, то
значение должно быть 1500000, если меньше 1000000, то NULL. например.
выходы

/****** Сценарий для команды SelectTopNRows из SSMS ******/
SELECT [BusinessEntityID], CASE WHEN SalesYTD >= 2000000 THEN SalesYTD ELSE CASE WHEN SalesYTD>=1000000 THEN 1500000 ELSE NULL END END as salesinfo
  FROM [AdventureWorks2016].[Sales].[SalesPerson]

Оценка уровня знаний Microsoft SQL Server (далее SQL)

  • Используйте код T-SQL
  • Обязательно используйте предоставленные имена файлов SQL
  • Загруженный вами ZIP-файл не должен содержать подкаталогов, только файлы
  • Убедитесь, что имена баз данных, пользователей и других объектов чувствительны к регистру символов
  • В запросах изменяйте названия столбцов, которые будут отображаться только в том случае, если этого специально требует задача
  • Запросы, которые будут отображаться в столбцы в порядке, указанном в упражнении появиться
  • Убедитесь, что вы всегда выбираете правильную базу данных
  • Используйте кодировку символов ASCII или UTF-8 (без BOM)
  • По умолчанию стартовой базой данных для MSSQL LOGIN, который "запускает" задания в контейнере Docker, являются первые 10 заданий: мастер.
  • В контейнере Docker стартовая база данных по умолчанию для MSSQL LOGIN, который "запускает" задания в контейнере Docker, составляет от 11) до 20) заданий: AdventureWorks.Для этих задач достаточно, если загруженный вами файл содержит только запрос.
  • Оценка файлов расширения sql в первую очередь автогрейдер остальные оцениваются вручную.

Задачи:

  1. На сервер добавляется диск, который еще не используется (20 ГБ). Установите его, создайте раздел размером 20 ГБ с файловой системой NTFS и добавьте S: монтируется как диск, а имя тома: SQLDATA должно быть.
Доказательства: Управление компьютером => Хранилище => Управление дисками
Имя файла: WINSQL-01.jpg
  1. S: диск для создания SQLDATA каталог, а затем еще два подкаталога в нем: DB и LOG с именем.
Доказательства: Открыв File Explorer => S: => каталог SQLDATA, вы увидите два запрашиваемых подкаталога.
Имя файла: WINSQL-02.jpg
  1. Войдите в сервер SQL (GREENFOX экземпляр) с помощью Microsoft SQL Server Management Studio!
Доказательства: Экземпляр GREENFOX, открытый в Microsoft SQL Server Management Studio.
Имя файла: WINSQL-03.jpg
  1. Извлечь C:\install\MSSQL\AdventureWorks2016.bak базы данных на экземпляр сервера GREENFOX AdventureWorks чтобы файлы базы данных хранились в S:\SQLDATA\DB, в то время как файлы журналов в S:\SQLDATA\LOG в библиотеку.

Gradescopeban вы можете найти резервную копию здесь: /AdventureWorks2016.bak, файлы базы данных: /SQLDATA/DB/AdventureWorks_Data.mdf, в то время как файлы журналов в /SQLDATA/LOG/AdventureWorks_Log.ldf файлы должны быть помещены в

Доказательства: Скопируйте или сохраните сценарий восстановления в Microsoft SQL Server Management Studio.
Имя файла: WINSQL-04.sql
USE [master]
RESTORE DATABASE [AdventureWorks] FROM DISK = N'/home/AdventureWorks2016.bak' WITH FILE = 1, MOVE N'AdventureWorks2016_Data' TO N'/SQLDATA/DB/AdventureWorks_Data.mdf', MOVE N'AdventureWorks2016_Log' TO N'/SQLDATA/LOG/AdventureWorks_Log.ldf', NOUNLOAD, STATS = 5

GO
  1. Создать isvc Аутентифицированная учетная запись MS SQL Server с минимальными (публичными) правами! Пароль должен быть: интерфАс21 Пароль не должен меняться пользователем при первом входе в систему.
Доказательства: Скопируйте или сохраните сценарий в Microsoft SQL Server Management Studio.
Имя файла: WINSQL-05.sql
USE [master]
GO
CREATE LOGIN [isvc] WITH PASSWORD=N'interfAce21', DEFAULT_DATABASE=[master], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF
GO
  1. Создать ИНТЕРФЕЙС со следующими настройками:
    1. Имя базы данных: INTERFACE
    2. Владелец: isvc
    3. Модель восстановления: FULL
    4. Начальный файл базы данных:
      1. размер: 64 МБайт
      2. местоположение: S:\SQLDATA\DB (Градкоскоп на /SQLDATA/DB)
    5. Файл журнала запуска:
      1. размер: 24 МБайт
      2. местоположение: S:\SQLDATA\LOG (Градкоскоп на /SQLDATA/LOG)
СОЗДАТЬ БАЗУ ДАННЫХ [ИНТЕРФЕЙС]
 СДЕРЖИВАНИЕ = НЕТ
 НА ПЕРВИЧНОЙ ОСНОВЕ
( NAME = N'INTERFACE', FILENAME = N'/SQLDATA/DB/INTERFACE.mdf' , SIZE = 65536KB , FILEGROWTH = 65536KB )
 ВОЙТИ В СИСТЕМУ
( NAME = N'INTERFACE_log', FILENAME = N'/SQLDATA/LOG/INTERFACE_log.ldf' , SIZE = 24576KB , FILEGROWTH = 65536KB )
GO

ИСПОЛЬЗОВАТЬ [ИНТЕРФЕЙС]
GO
ALTER AUTHORIZATION ON DATABASE::[INTERFACE] TO [isvc]

ALTER DATABASE [INTERFACE] SET COMPATIBILITY_LEVEL = 150
GO
ALTER DATABASE [INTERFACE] SET ANSI_NULL_DEFAULT OFF
GO
ALTER DATABASE [INTERFACE] SET ANSI_NULLS OFF
GO
ALTER DATABASE [INTERFACE] SET ANSI_PADDING OFF
GO
ALTER DATABASE [INTERFACE] SET ANSI_WARNINGS OFF
GO
ALTER DATABASE [INTERFACE] SET ARITHABORT OFF
GO
ALTER DATABASE [INTERFACE] SET AUTO_CLOSE OFF
GO
ALTER DATABASE [INTERFACE] SET AUTO_SHRINK OFF
GO
ALTER DATABASE [INTERFACE] SET AUTO_CREATE_STATISTICS ON(INCREMENTAL = OFF)
GO
ALTER DATABASE [INTERFACE] SET AUTO_UPDATE_STATISTICS ON
GO
ALTER DATABASE [INTERFACE] SET CURSOR_CLOSE_ON_COMMIT OFF
GO
ALTER DATABASE [INTERFACE] SET CURSOR_DEFAULT GLOBAL
GO
ALTER DATABASE [INTERFACE] SET CONCAT_NULL_YIELDS_NULL OFF
GO
ALTER DATABASE [INTERFACE] SET NUMERIC_ROUNDABORT OFF
GO
ALTER DATABASE [INTERFACE] SET QUOTED_IDENTIFIER OFF
GO
ALTER DATABASE [INTERFACE] SET RECURSIVE_TRIGGERS OFF
GO
ALTER DATABASE [INTERFACE] SET DISABLE_BROKER
GO
ALTER DATABASE [INTERFACE] SET AUTO_UPDATE_STATISTICS_ASYNC OFF
GO
ALTER DATABASE [INTERFACE] SET DATE_CORRELATION_OPTIMIZATION OFF
GO
ALTER DATABASE [INTERFACE] SET PARAMETERIZATION SIMPLE
GO
ALTER DATABASE [INTERFACE] SET READ_COMMITTED_SNAPSHOT OFF
GO
ALTER DATABASE [INTERFACE] SET READ_WRITE
GO
ALTER DATABASE [INTERFACE] SET RECOVERY FULL
GO
ALTER DATABASE [INTERFACE] SET MULTI_USER
GO
ALTER DATABASE [INTERFACE] SET PAGE_VERIFY CHECKSUM
GO
ALTER DATABASE [INTERFACE] SET TARGET_RECOVERY_TIME = 60 SECONDS
GO
ALTER DATABASE [INTERFACE] SET DELAYED_DURABILITY = DISABLED
GO
ИСПОЛЬЗОВАТЬ [ИНТЕРФЕЙС]
GO
ALTER DATABASE SCOPED CONFIGURATION SET LEGACY_CARDINALITY_ESTIMATION = Off;
GO
ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET LEGACY_CARDINALITY_ESTIMATION = Primary;
GO
ALTER DATABASE SCOPED CONFIGURATION SET MAXDOP = 0;
GO
ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET MAXDOP = PRIMARY;
GO
ALTER DATABASE SCOPED CONFIGURATION SET PARAMETER_SNIFFING = On;
GO
ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET PARAMETER_SNIFFING = Primary;
GO
ALTER DATABASE SCOPED CONFIGURATION SET QUERY_OPTIMIZER_HOTFIXES = Off;
GO
ALTER DATABASE SCOPED CONFIGURATION FOR SECONDARY SET QUERY_OPTIMIZER_HOTFIXES = Primary;
GO
ИСПОЛЬЗОВАТЬ [ИНТЕРФЕЙС]
GO
IF NOT EXISTS (SELECT name FROM sys.filegroups WHERE is_default=1 AND name = N'PRIMARY') ALTER DATABASE [INTERFACE] MODIFY FILEGROUP [PRIMARY] DEFAULT
GO
  1. Предоставьте права только на запросы isvc учитывать AdventureWorks база данных HumanResources в схеме vEmployee в свой список!
Доказательства: Скопируйте или сохраните сценарий в Microsoft SQL Server Management Studio.
Имя файла: WINSQL-07.sql
USE [AdventureWorks]
GO
CREATE USER [isvc] FOR LOGIN [isvc]
GO
GRANT SELECT ON [HumanResources].[vEmployee] TO [isvc]
GO
  1. ИНТЕРФЕЙС базу данных для создания База данных AWdatabase схема!
Доказательства: Microsoft SQL Server Management Studio в экземпляре GREENFOX в базе данных INTERFACE схема базы данных AWdatabase сценарий "Create To".
Имя файла: WINSQL-08.sql
ИСПОЛЬЗОВАТЬ [ИНТЕРФЕЙС]
GO

/****** Объект: schema [dbo] Script Date: 2023 Jan 15 13:45:32 ******/
CREATE SCHEMA [AWdatabase]
GO
  1. С сайта База данных AWdatabase схема для создания синоним AW_Employee который является AdventureWorks база данных HumanResources в схеме vEmployee указывает на область просмотра!
Доказательства: Microsoft SQL Server Management Studio в экземпляре GREENFOX базы данных INTERFACE AWdatabase schema AW_Employee синоним сценария "Create To".
Имя файла: WINSQL-09.sql
ИСПОЛЬЗОВАТЬ [ИНТЕРФЕЙС]
GO
CREATE SYNONYM [AWdatabase].[AW_Employee ] FOR [AdventureWorks].[HumanResources].[vEmployee]
GO
  1. Сделать ИНТЕРФЕЙС создайте ПОЛНУЮ резервную копию базы данных следующим образом:
  1. Имя файла резервного копирования: S:\SQLDATA\INTERFACE.bak (В Gradescope /var/backups/INTERFACE.bak)
  2. При сохранении перезаписывается одноименный медиа-набор!
Доказательства: В Microsoft SQL Server Management Studio в экземпляре GREENFOX сценарий резервного копирования базы данных INTERFACE.
Имя файла: WINSQL-10.sql
BACKUP DATABASE [INTERFACE] TO DISK = N'/var/backups/INTERFACE.bak' WITH NOFORMAT, INIT, NAME = N'INTERFACE-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10
GO
  1. Постройте SQL-запрос, который вычисляет квадратный корень из 25!
Доказательства: Скопируйте или сохраните сценарий в Microsoft SQL Server Management Studio.
Имя файла: WINSQL-11.sql
ВЫБЕРИТЕ SQRT(25)
  1. Создайте SQL-запрос, который возвращает сегодняшнюю дату в данном формате: 2022-10-25
Доказательства: Скопируйте или сохраните сценарий в Microsoft SQL Server Management Studio.
Имя файла: WINSQL-12.sql
SELECT CAST(GETDATE() as date)
  1. Создайте SQL-запрос, который подсчитывает, сколько дней прошло с 25 апреля 1994 года!
Доказательства: Скопируйте или сохраните сценарий в Microsoft SQL Server Management Studio.
Имя файла: WINSQL-13.sql
SELECT DATEDIFF(day,'2022-04-25',GETDATE())
  1. Создайте SQL-запрос, который возвращает ранее загруженные AdventureWorks база данных Person.Address таблицу (все поля - не меняйте порядок столбцов)!
Доказательства: Скопируйте или сохраните сценарий в Microsoft SQL Server Management Studio.
Имя файла: WINSQL-14.sql
/****** Сценарий для команды SelectTopNRows из SSMS ******/
USE [AdventureWorks]
GO
ВЫБЕРИТЕ ВЕРШИНУ (15) *
  FROM [AdventureWorks].[Person].[Address]
  1. Создайте SQL-запрос, который возвращает ранее загруженные AdventureWorks база данных Person.Person с сайта первые 5 элементов(все поля - не меняйте порядок столбцов), но так, чтобы там, где PersonType значение поля 'EM‘!
Доказательства: Скопируйте или сохраните сценарий в Microsoft SQL Server Management Studio.
Имя файла: WINSQL-15.sql
USE [AdventureWorks]
GO
ВЫБЕРИТЕ ВЕРШИНУ (5) *
  FROM [AdventureWorks].[Person].[Person].[Person]
  WHERE PersonType='EM'
  1. Создайте SQL-запрос, который возвращает ранее загруженные AdventureWorks база данных Person.Person с сайта первые 30 пунктовно так, чтобы там, где PersonType значение поля 'EM' и только PersonType, FirstName, LastName поля в результате запроса!
Доказательства: Скопируйте или сохраните сценарий в Microsoft SQL Server Management Studio.
Имя файла: WINSQL-16.sql
USE [AdventureWorks]
GO
SELECT TOP (30) PersonType, FirstName, LastName
  FROM [AdventureWorks].[Person].[Person].[Person]
  WHERE PersonType='EM'
  1. Создайте SQL-запрос, который возвращает ранее загруженные AdventureWorks база данных Person.Person с сайта первые 20 пунктовно так, чтобы там, где PersonType значение поля 'EM' и только PersonType, FirstName, LastName поля в результате запроса, но Имя и Фамилия должны располагаться в столбце, разделенные одним символом пробела, Полное имя под именем!
Доказательства: Скопируйте или сохраните сценарий в Microsoft SQL Server Management Studio.
Имя файла: WINSQL-17.sql
USE [AdventureWorks]
GO
SELECT TOP (20) PersonType, FirstName+ ' ' + LastName как FullName
  FROM [AdventureWorks].[Person].[Person].[Person]
  WHERE PersonType='EM'
  1. Создайте SQL-запрос, который возвращает ранее загруженные AdventureWorks база данных Person.Person таблицы (все столбцы, все поля - не меняйте порядок столбцов), где PersonType значение поля 'EM' и FirstName поле содержит только 'Джон' должен быть включен!
Доказательства: Скопируйте или сохраните сценарий в Microsoft SQL Server Management Studio.
Имя файла: WINSQL-18.sql
USE [AdventureWorks]
GO
SELECT *
  FROM [AdventureWorks].[Person].[Person].[Person]
  WHERE PersonType='EM' AND FirstName='John'
  1. Создайте SQL-запрос, который возвращает ранее загруженные AdventureWorks база данных Person.Person таблицы (все столбцы, все поля - не меняйте порядок столбцов), где PersonType значение поля 'EM' и FirstName поле содержит только 'Джон', должны отображаться только те, где Фамилия значение поля 'eКонец персонажа!
Доказательства: Скопируйте или сохраните сценарий в Microsoft SQL Server Management Studio.
Имя файла: WINSQL-19.sql
USE [AdventureWorks]
GO
SELECT *
  FROM [AdventureWorks].[Person].[Person].[Person]
  WHERE PersonType='EM' AND FirstName='John' AND RIGHT(LastName,1)='e'
  1. Посчитайте, сколько 'Кен' имя (FirstName) запись является ранее загруженной AdventureWorks база данных Person.Person в вашем совете!
Доказательства: Скопируйте или сохраните сценарий в Microsoft SQL Server Management Studio.
Имя файла: WINSQL-20.sql
USE [AdventureWorks]
GO
ВЫБЕРИТЕ COUNT(*)
  FROM [AdventureWorks].[Person].[Person].[Person]
  WHERE FirstName='Ken'