курсач / Наволоцкий_1302_v2
.pdf04_ViewsAndFunctions.sql
/*
============================================================
=====================
СКРИПТ 04: ПРЕДСТАВЛЕНИЯ И ФУНКЦИИ (VIEWS & UDF)
Схемы: [Stock]
============================================================
===================== */
USE CircuitDB;
GO
PRINT '=========================================================== ==';
PRINT 'Создание функций и представлений...';
PRINT 'Время запуска: ' + CONVERT(VARCHAR(30), GETDATE(), 120);
PRINT '=========================================================== ==';
GO
/* =============================================
1. SCALAR FUNCTION (Скалярная функция)
Тип: Простая Задача: Склеить значение и единицу измерения.
============================================= */
91
IF OBJECT_ID('[Stock].[udf_FormatValue]', 'FN') IS NOT NULL
DROP FUNCTION [Stock].[udf_FormatValue];
GO
CREATE FUNCTION [Stock].[udf_FormatValue]
(
@Value NVARCHAR(100),
@Unit NVARCHAR(20)
)
RETURNS NVARCHAR(150)
AS
BEGIN
DECLARE @Result NVARCHAR(150);
-- Логика: Если единицы измерения нет, возвращаем просто число
IF @Unit IS NULL OR @Unit = ''
SET @Result = @Value;
ELSE
SET @Result = @Value + ' ' + @Unit;
RETURN @Result;
END
GO
PRINT 'Function [udf_FormatValue] created.';
GO
/* =============================================
2. INLINE TABLE-VALUED FUNCTION (Простая табличная)
92
Тип: Inline TVF (содержит только RETURN SELECT)
Задача: Получить параметры компонента в отформатированном виде.
============================================= */
IF OBJECT_ID('[Stock].[udf_GetComponentParams]', 'IF') IS
NOT NULL DROP FUNCTION [Stock].[udf_GetComponentParams];
GO
CREATE FUNCTION [Stock].[udf_GetComponentParams]
(
@CompID INT
)
RETURNS TABLE
AS
RETURN
(
SELECT
ParamName AS [Parameter],
-- Вызов скалярной функции внутри табличной
[Stock].[udf_FormatValue](ParamValue, Unit) AS [ValueFormatted]
FROM [Stock].[ComponentParams]
WHERE ComponentID = @CompID
); GO
PRINT 'Function [udf_GetComponentParams] created.'; GO
/* =============================================
93
3. MULTI-STATEMENT TABLE-VALUED FUNCTION (Сложная табличная)
Тип: Multi-Statement (создает переменную-таблицу @ret)
Задача: "Досье" на компонент. Анализирует качество заполнения данных.
Требование: "табличная, которая создается"
============================================= */
IF OBJECT_ID('[Stock].[udf_GetComponentQualityReport]', 'TF') IS NOT NULL DROP FUNCTION [Stock].[udf_GetComponentQualityReport];
GO
CREATE FUNCTION [Stock].[udf_GetComponentQualityReport]
(
@CategoryID INT -- Фильтр по категории
)
RETURNS @ReportTable TABLE
(
PartNumber NVARCHAR(100),
Manufacturer NVARCHAR(100),
HasDescription BIT,
HasModel BIT,
QualityScore INT, -- Вычисляемый балл (0-100) Verdict NVARCHAR(50)
)
AS
BEGIN
-- 1. Первичная вставка данных
94
INSERT INTO @ReportTable (PartNumber, Manufacturer,
HasDescription, HasModel, QualityScore, Verdict)
SELECT c.PartNumber, m.Name,
CASE WHEN c.Description IS NOT NULL THEN 1 ELSE 0
END,
CASE WHEN EXISTS(SELECT 1 FROM [Stock].[ComponentModels] cm WHERE cm.ComponentID = c.ComponentID) THEN 1 ELSE 0 END,
0, -- Начальный балл
'Unknown'
FROM [Stock].[Components] c
JOIN [Ref].[Manufacturers] m ON c.ManufacturerID = m.ManufacturerID
WHERE c.CategoryID = @CategoryID;
--2. Логика расчета баллов (Multi-step logic)
--+40 баллов, если есть описание
UPDATE @ReportTable SET QualityScore = QualityScore + 40 WHERE HasDescription = 1;
-- +60 баллов, если есть мат. модель
UPDATE @ReportTable SET QualityScore = QualityScore + 60 WHERE HasModel = 1;
-- 3. Вынесение вердикта
UPDATE @ReportTable
95
SET Verdict = CASE
WHEN QualityScore = 100 THEN 'Excellent'
WHEN QualityScore >= 40 THEN 'Good'
ELSE 'Poor Data'
END;
RETURN;
END
GO
PRINT 'Function [udf_GetComponentQualityReport] created.'; GO
/* =============================================
4. VIEW 1: REPORT (Для отчетов и XML)
Обычное соединение таблиц (INNER JOIN)
Имена полей на английском (для совместимости с процедурой экспорта)
============================================= */
IF OBJECT_ID('[Stock].[v_FullComponentReport]', 'V') IS NOT
NULL DROP VIEW [Stock].[v_FullComponentReport];
GO
CREATE VIEW [Stock].[v_FullComponentReport]
AS
SELECT c.ComponentID,
c.PartNumber, -- В XML уйдет как @Article m.Name AS Manufacturer,
cat.Name AS Category,
96
p.Name AS Package, c.Description, CASE
WHEN c.IsActive = 1 THEN 'Active'
ELSE 'Obsolete'
END AS Status
FROM [Stock].[Components] c
INNER JOIN [Ref].[Manufacturers] m ON c.ManufacturerID = m.ManufacturerID
INNER JOIN [Ref].[Categories] cat ON c.CategoryID = cat.CategoryID
INNER JOIN [Ref].[PackageTypes] p ON c.PackageID = p.PackageID;
GO
PRINT 'View [v_FullComponentReport] created.'; GO
/* =============================================
5. VIEW 2: ENGINEERING FILTER (Инженерный фильтр)
Фильтрация + JOIN
============================================= */
IF OBJECT_ID('[Stock].[v_ActiveComponentsSpiceready]', 'V') IS NOT NULL DROP VIEW [Stock].[v_ActiveComponentsSpiceready];
GO
CREATE VIEW [Stock].[v_ActiveComponentsSpiceready]
AS
SELECT
97
c.PartNumber, cat.Name AS Category,
m.Name AS Manufacturer, sim.Name AS SimulationType, sim.Engine
FROM [Stock].[Components] c
INNER JOIN [Stock].[ComponentModels] cm ON c.ComponentID = cm.ComponentID
INNER JOIN [Ref].[SimulationTypes] sim ON cm.SimTypeID = sim.SimTypeID
JOIN [Ref].[Categories] cat ON c.CategoryID = cat.CategoryID JOIN [Ref].[Manufacturers] m ON c.ManufacturerID = m.ManufacturerID
WHERE
c.IsActive = 1
AND sim.Name LIKE '%SPICE%';
GO
PRINT 'View [v_ActiveComponentsSpiceready] created.'; GO
/* =============================================
6. VIEW 3: ANALYTICS (Сложная аналитика)
Требование: "CTE, GROUP BY, HAVING"
Смысл: Показывает категории, где более 0 компонентов,
и считает сколько активных позиций.
============================================= */
IF OBJECT_ID('[Stock].[v_CategoryAnalytics]', 'V') IS NOT
NULL DROP VIEW [Stock].[v_CategoryAnalytics];
GO
98
CREATE VIEW [Stock].[v_CategoryAnalytics]
AS
WITH CategoryCountsCTE AS (
-- Common Table Expression для предварительного подсчета
SELECT
CategoryID,
COUNT(*) AS TotalCount,
SUM(CASE WHEN IsActive = 1 THEN 1 ELSE 0 END) AS
ActiveCount
FROM [Stock].[Components]
GROUP BY CategoryID
)
SELECT
cat.Name AS CategoryName, cte.TotalCount, cte.ActiveCount,
-- Вычисляем процент активных
CAST((cte.ActiveCount * 100.0 / cte.TotalCount) AS DECIMAL(5,2)) AS ActivePercent
FROM CategoryCountsCTE cte
JOIN [Ref].[Categories] cat ON cte.CategoryID = cat.CategoryID
-- Требование: HAVING (фильтруем группы уже после агрегации,
хотя здесь CTE, но смысл тот же)
-- Для демонстрации HAVING добавим условие: показать только те, где есть хоть 1 компонент
WHERE cte.TotalCount > 0;
GO
99
PRINT 'View [v_CategoryAnalytics] created.';
GO
PRINT '=========================================================== ==';
PRINT 'Скрипт 04 выполнен успешно.';
PRINT 'Время завершения: ' + CONVERT(VARCHAR(30), GETDATE(), 120);
PRINT '=========================================================== ==';
GO
100
