Создайте строку для каждого месяца года, если продукт продан в этом году

У меня есть вопрос. На основе моих фиктивных данных я хотел бы создать представление, которое

  1. Показывает всех клиентов, даже тех, кто ничего не продал, поэтому это всего одна строка, например «V.Z.W. Фредерик», а остальное для «Кевин Н.В.».
  2. Сочетание всех годов/месяцев и продуктов, которые когда-то были проданы этим клиентом в этом конкретном году. Например, продукт с идентификатором «9000» был продан в годах/месяцах «202303» и «202304» компанией «Kevin N.V.». но я все равно хочу увидеть комбинацию этого продукта и всех годов/месяцев этого года для этого клиента. Продукт с идентификатором «9001» был продан только в годах/месяцах «202403» и «202404», поэтому я хочу видеть только комбинацию всех годов/месяцев того года, когда этот продукт продается этим покупателем. Продукт с идентификатором «9002» был продан в оба года, поэтому существует комбинация года/месяца обоих лет. Продажи должны быть равны 0 за этот конкретный год/месяцы, например «202301» = 0, «

DIM_CUSTOMER

DECLARE @DIM_CUSTOMERS TABLE([BusinessKey] INT,[Customer] NVARCHAR(255))
 
INSERT INTO @DIM_CUSTOMERS
VALUES 
(10000, 'Kevin N.V.'),
(10001, 'V.Z.W. Frederik'),
(10002, 'Klaas N.V.')
SELECT * FROM @DIM_CUSTOMERS

DIM_PRODUCT

DECLARE @DIM_PRODUCTS TABLE([BusinessKey] INT, [Product] NVARCHAR(255))
 
INSERT INTO @DIM_PRODUCTS
VALUES
(9000, 'PH114'),
(9001, 'PH272'),
(9002, 'PH878')
SELECT * FROM @DIM_PRODUCTS

DIM_DATES

DECLARE @DIM_DATES TABLE([BusinessKey] INT, [Year] INT, [Month] INT, [YearMonth] INT, [YearMonthText] NVARCHAR(20))

INSERT INTO @DIM_DATES
VALUES
(202301, 2023, 1, 202301, '2023.01'),
(202302, 2023, 2, 202302, '2023.02'),
(202303, 2023, 3, 202303, '2023.03'),
(202304, 2023, 4, 202304, '2023.04'),
(202305, 2023, 5, 202305, '2023.05'),
(202306, 2023, 6, 202306, '2023.06'),
(202307, 2023, 7, 202307, '2023.07'),
(202308, 2023, 8, 202308, '2023.08'),
(202309, 2023, 9, 202309, '2023.09'),
(202310, 2023, 10, 202310, '2023.10'),
(202311, 2023, 11, 202311, '2023.11'),
(202312, 2023, 12, 202312, '2023.12'),
(202401, 2024, 1, 202401, '2024.01'),
(202402, 2024, 2, 202402, '2024.02'),
(202403, 2024, 3, 202403, '2024.03'),
(202404, 2024, 4, 202404, '2024.04'),
(202405, 2024, 5, 202405, '2024.05'),
(202406, 2024, 6, 202406, '2024.06'),
(202407, 2024, 7, 202407, '2024.07'),
(202408, 2024, 8, 202408, '2024.08'),
(202409, 2024, 9, 202409, '2024.09'),
(202410, 2024, 10, 202410, '2024.10'),
(202411, 2024, 11, 202411, '2024.11'),
(202412, 2024, 12, 202412, '2024.12')
SELECT * FROM @DIM_DATES

ФАКТ_ПРОДАЖИ

DECLARE @FACT_SALES TABLE([ID] INT, [FK_Product] INT, [FK_Customer] INT, [FK_Date] INT, [Sales] FLOAT)

INSERT INTO @FACT_SALES
VALUES
(1, 9000, 10000, 202303, 90.48),
(2, 9000, 10000, 202304, 20.40),
(3, 9002, 10000, 202305, 250.85),
(4, 9002, 10000, 202303, 100.50),
(5, 9000, 10000, 202403, 38.40),
(6, 9000, 10000, 202406, 474.50),
(7, 9001, 10000, 202403, 128.60),
(8, 9001, 10000, 202404, 144.97),
(9, 9000, 10002, 202303, 199.60),
(10, 9001, 10002, 202302, 58.97),
(11, 9001, 10002, 202402, 40.88)
SELECT * FROM @FACT_SALES

Это пример того, что я хочу, на мой взгляд:

|   Customer    |   Product |   YearMonthText   |   Sales   |
|   -------------------------   |   ---------------------   |   ---------------------   |   ---------------------   |
|   Kevin N.V.  |   PH114   |   202301  |   0   |
|   Kevin N.V.  |   PH114   |   202302  |   0   |
|   Kevin N.V.  |   PH114   |   202303  |   90  |

Когда я воссоздаю обычное представление, я вижу только данные, которые были проданы за этот конкретный год/месяц. Мне удалось сделать это в Power BI с DAX (ALLSELECTED), но я хочу сделать то же самое в SQL Server для повышения производительности. Я попытался создать что-то в T-SQL, чтобы начать с таблицы клиентов или использовать CROSS JOINS, но не удалось получить результаты.

Может ли кто-нибудь помочь мне с этой проблемой?

Спасибо!

Пожалуйста, поделитесь своей попыткой.

Mitch Wheat 05.06.2024 08:46

Пожалуйста, покажите (в виде уценки таблицы) желаемые результаты и покажите нам свою попытку.

Dale K 05.06.2024 08:46

Я специально сказал, что данные должны быть в уценке таблицы, а не в виде изображения - это правило сайта, а не мое.

Dale K 05.06.2024 09:24
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
123
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проверьте эту скрипку, чтобы увидеть это в действии. Это не самое элегантное решение, но оно работает.

https://dbfiddle.uk/oGHeZwYK

При этом используется пара CTE для

  1. CustProdYears Получите уникальный список клиентов, продуктов и лет, чтобы мы могли ограничить результаты только этими клиентами, продуктами и годами. Это сделано для того, чтобы год не появлялся, если клиент не продал определенный продукт в этом году.

  2. CustomerSales Получите список всех продаж, в которых клиент, продукт и год указаны в CustProdYears выше.

WITH CustProdYears 
    AS(
        SELECT DISTINCT 
            d.[Year] as SaleYear, s.FK_Product, s.FK_Customer
            FROM @FACT_SALES s       
            JOIN @DIM_DATES d on s.FK_Date = d.BusinessKey
    )
, CustomerSales 
    AS (
        SELECT cpy.FK_Customer, cpy.FK_Product, d.YearMonthText, s.Sales
            FROM CustProdYears cpy 
            JOIN @DIM_DATES d on cpy.[SaleYear] = d.[Year]
            LEFT JOIN @FACT_SALES s 
                on s.FK_Customer = cpy.FK_Customer
                and s.FK_Product = cpy.FK_Product
                and s.FK_Date = d.BusinessKey
    )

SELECT 
      Customer, Product, YearMonthText
    , Sales = ISNULL(Sales, 0)
    FROM @DIM_CUSTOMERS c 
        LEFT JOIN CustomerSales s ON c.BusinessKey = s.FK_Customer
        LEFT JOIN @DIM_PRODUCTS p on s.FK_Product = p.BusinessKey
    ORDER BY Customer, Product, YearMonthText

Последний ВЫБОР просто начинается с клиентов (поэтому все они появляются в результатах), а затем слева присоединяется к нашему CustomerSales CTE, чтобы показать все связанные продукты/годы (и продажи, если они есть).

Большое спасибо за ваш ответ. Это действительно правильный результат, и с точки зрения производительности определенно лучше сделать это непосредственно в представлении SQL, чем создавать показатели в Power BI.

KevinN1992 06.06.2024 08:24

Предположим, у меня есть дополнительная таблица FACT_BUDGET с теми же столбцами DIM, но со столбцом Budget, и в конце я хочу объединить оба столбца Sales и «Бюджет» на основе тех же столбцов DIM. Это хороший способ повторить этот запрос, как в вашем ответе на FACT_BUDGET, и в конце выполнить соединение между двумя таблицами, или можно ли продолжить работу над этим оператором SELECT и просто соединить его с помощью FACT_BUDGET. Я спрашиваю об этом, потому что иногда у клиента может быть budget, но не быть sales, или и то и другое, или и то, и другое NULL.

KevinN1992 06.06.2024 15:58

Я не уверен, что точно понимаю ваш сценарий, но вы можете создать представление, состоящее из двух таблиц. Возможно, вы захотите добавить справочный столбец, чтобы знать, откуда взялась каждая сумма. Что-то вроде UNION. Затем вы можете заменить таблицу продаж на новое представление CREATE VIEW SalesBudget AS SELECT 'Sales' as dataSource, ID, Product, Customer, Date, Sales FROM FACT_SALES UNION ALL SELECT 'Budget' as dataSource, ID, Product, Customer, Date, Sales FROM FACT_BUDGET.

Alan Schofield 06.06.2024 16:32

Другие вопросы по теме