← Назад к вопросам

Какие запросы базе данных писал?

2.0 Middle🔥 241 комментариев
#Entity Framework и ORM#Базы данных и SQL

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Мой опыт работы с запросами к базам данных

За мою карьеру я писал огромное количество SQL-запросов различной сложности для разных СУБД, но преимущественно для Microsoft SQL Server и PostgreSQL, поскольку они наиболее распространены в .NET экосистеме. Вот основные категории запросов, с которыми я работал:

1. Базовые CRUD-операции

Это основа любого backend-приложения. Я писал все виды запросов для манипуляции данными:

-- SELECT с фильтрацией, сортировкой и пагинацией
SELECT Id, Name, Email, CreatedAt 
FROM Users 
WHERE IsActive = 1 
ORDER BY CreatedAt DESC 
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

-- INSERT с возвратом сгенерированного ID
INSERT INTO Products (Name, Price, CategoryId) 
VALUES ('New Product', 99.99, 5)
RETURNING Id;

-- UPDATE с джойнами
UPDATE Orders o
SET Status = 'Completed'
FROM Customers c
WHERE o.CustomerId = c.Id 
  AND c.Country = 'USA' 
  AND o.OrderDate < '2023-01-01';

-- DELETE с каскадными ограничениями
DELETE FROM ShoppingCarts 
WHERE LastActivityDate < DATEADD(month, -6, GETDATE());

2. Сложные JOIN-запросы

Работа с реляционными данными требует мастерства в объединениях таблиц:

-- Множественные INNER и LEFT JOIN
SELECT 
    o.OrderId,
    o.OrderDate,
    c.CustomerName,
    e.EmployeeName,
    SUM(od.Quantity * p.UnitPrice) as TotalAmount
FROM Orders o
INNER JOIN Customers c ON o.CustomerId = c.CustomerId
LEFT JOIN Employees e ON o.EmployeeId = e.EmployeeId
INNER JOIN OrderDetails od ON o.OrderId = od.OrderId
INNER JOIN Products p ON od.ProductId = p.ProductId
GROUP BY o.OrderId, o.OrderDate, c.CustomerName, e.EmployeeName
HAVING SUM(od.Quantity * p.UnitPrice) > 1000;

3. Агрегирующие запросы и аналитика

Для отчетов и дашбордов часто использовал оконные функции:

-- Оконные функции для аналитики
SELECT 
    CustomerId,
    OrderDate,
    TotalAmount,
    SUM(TotalAmount) OVER(PARTITION BY CustomerId ORDER BY OrderDate) as RunningTotal,
    AVG(TotalAmount) OVER(PARTITION BY CustomerId) as CustomerAverage,
    ROW_NUMBER() OVER(PARTITION BY CustomerId ORDER BY OrderDate DESC) as RecentOrderIndex
FROM Orders;

4. Оптимизированные запросы

Важной частью работы была оптимизация производительности:

-- Использование CTE для сложных иерархических запросов
WITH RecursiveCTE AS (
    -- Якорная часть
    SELECT EmployeeId, ManagerId, 1 as Level
    FROM Employees
    WHERE ManagerId IS NULL
    
    UNION ALL
    
    -- Рекурсивная часть
    SELECT e.EmployeeId, e.ManagerId, r.Level + 1
    FROM Employees e
    INNER JOIN RecursiveCTE r ON e.ManagerId = r.EmployeeId
)
SELECT * FROM RecursiveCTE;

-- Оптимизация с временными таблицами для сложных вычислений
CREATE TABLE #TempSalesData (
    ProductId INT,
    TotalSales DECIMAL(18,2),
    SalesCount INT
);

INSERT INTO #TempSalesData
SELECT ProductId, SUM(Amount), COUNT(*)
FROM Sales
WHERE SaleDate BETWEEN @StartDate AND @EndDate
GROUP BY ProductId;

5. Транзакционные запросы

Для обеспечения ACID-свойств и целостности данных:

BEGIN TRANSACTION;
BEGIN TRY
    -- Резервируем товар
    UPDATE Inventory 
    SET Quantity = Quantity - @OrderedQuantity
    WHERE ProductId = @ProductId AND Quantity >= @OrderedQuantity;
    
    -- Создаем заказ
    INSERT INTO Orders (CustomerId, ProductId, Quantity, OrderDate)
    VALUES (@CustomerId, @ProductId, @OrderedQuantity, GETDATE());
    
    -- Фиксируем изменения
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION;
    THROW;
END CATCH;

6. Запросы для миграций и обслуживания

Часто писал DDL-запросы для изменения структуры БД:

-- Создание индексов для оптимизации
CREATE NONCLUSTERED INDEX IX_Orders_CustomerId_OrderDate 
ON Orders(CustomerId, OrderDate DESC)
INCLUDE (TotalAmount, Status);

-- Партиционирование больших таблиц
CREATE PARTITION FUNCTION OrderDateRangePF (DATE)
AS RANGE RIGHT FOR VALUES (
    '2022-01-01', '2023-01-01', '2024-01-01'
);

7. Интеграция с Entity Framework Core

В .NET-проектах активно использовал LINQ to SQL и Entity Framework Core:

// Сложные запросы через LINQ
var result = await context.Orders
    .Where(o => o.OrderDate >= startDate && o.OrderDate <= endDate)
    .GroupBy(o => new { o.CustomerId, o.OrderDate.Year, o.OrderDate.Month })
    .Select(g => new 
    {
        g.Key.CustomerId,
        g.Key.Year,
        g.Key.Month,
        TotalAmount = g.Sum(o => o.TotalAmount),
        OrderCount = g.Count()
    })
    .OrderByDescending(x => x.TotalAmount)
    .Take(100)
    .ToListAsync();

// Raw SQL через EF Core при сложных запросах
var reports = await context.SalesReports
    .FromSqlRaw(@"EXEC GenerateSalesReport @StartDate, @EndDate, @RegionId", 
        parameters)
    .ToListAsync();

Основные принципы, которых я придерживаюсь:

  • Безопасность: Всегда использую параметризованные запросы для предотвращения SQL-инъекций
  • Производительность: Анализирую execution plan, добавляю индексы, избегаю N+1 проблем
  • Читаемость: Форматирую сложные запросы, добавляю комментарии
  • Масштабируемость: Учитываю блокировки и изоляцию транзакций
  • Поддержка: Пишу идемпотентные миграционные скрипты

В зависимости от проекта, также работал с хранимыми процедурами, триггерами, представлениями, функциями и материализованными представлениями. Для NoSQL баз данных (в основном MongoDB и Redis) использовал соответствующие query language и паттерны доступа к данным.