Отображать клиента и все даты его заказа в одной строке

#sql #snowflake-cloud-data-platform

#sql #snowflake-cloud-data-platform

Вопрос:

У меня есть таблица клиентов и таблица заказов. Я хочу отобразить клиента и все даты его / ее заказа в одной строке, а не в нескольких строках. Вот что у меня есть и что я ищу:

Изображение обеих таблиц, текущие результаты, то, что я ищу

Базовый код для получения результатов:

 select customerid, name, orderdate
from customer_table c inner join
     order_table o
     on c.customerid = o.customerid 
  

Комментарии:

1. Что делать, если клиент разместил 10 заказов? Вам нужно 10 столбцов?

2. Да, но максимальное количество заказов будет только 12.

3. Какую СУБД вы используете? MySQL, Oracle, Microsoft SQL Server и т. Д

4. Это хранилище данных snowflake, поэтому ANSI SQL

Ответ №1:

это будет работать максимум, если вы не сможете отобразить его в разных столбцах с нулевыми значениями:

 select customer_id,name,LISTAGG(orderdate, ', ') WITHIN GROUP (ORDER BY orderdate) 
from(select customerid, name, orderdate
from customer_table c inner join
order_table o
on c.customerid = o.customerid );
  

Ответ №2:

Вы можете использовать следующее, поскольку вы ограничены лимитом в 12 заказов. Если вы расширяетесь до неизвестного количества заказов без верхнего предела, то вам нужно будет использовать динамический SQL, и даже тогда это будет сложно, потому что вам также потребуется динамически создавать уникальные имена столбцов.

Этот запрос распределяется по заказчикам и задает значения заказа. Они будут находиться в датированном порядке и будут иметь значение NULL, если заказов больше не будет. Это как бы предполагает, что по крайней мере у одного клиента есть 12 заказов. Вы получите столбец всех нулей, если это не так

 IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Results
IF OBJECT_ID('tempdb..#sortedRows') IS NOT NULL DROP TABLE #SortedRows
DECLARE @CustomerList TABLE(CustomerID INT, RowNo INT); 
INSERT INTO @CustomerList SELECT DISTINCT CustomerID, ROW_NUMBER() OVER(ORDER BY CustomerID) RowNo FROM Customer_Table (NOLOCK)
DECLARE @Count INT = (SELECT COUNT(DISTINCT CustomerID) RowNumber FROM @CustomerList)
DECLARE @Counter INT = 0
DECLARE @CustToProcess INT
CREATE TABLE #Results(CustomerID INT, [Name] VARCHAR(50), OrderDate1 DATETIME, 
OrderDate2 DATETIME, OrderDate3 DATETIME, OrderDate4 DATETIME, OrderDate5 DATETIME, 
OrderDate6 DATETIME, OrderDate7 DATETIME, OrderDate8 DATETIME, OrderDate9 DATETIME, 
OrderDate10 DATETIME, OrderDate11 DATETIME, OrderDate12 DATETIME) 
INSERT INTO #Results(CustomerID, Name) SELECT DISTINCT CustomerID, Name FROM Customer_Table

SELECT ROW_NUMBER() OVER(PARTITION BY c.CustomerID ORDER BY OrderDate) RowNo,
c.CustomerID, c.Name, t.OrderDate  INTO #SortedRows
FROM Customer_Table c (NOLOCK) JOIN Order_Table t ON c.CustomerID = t.CustomerID 

WHILE @Counter < @Count
BEGIN
    SET @Counter  = 1
    SET @CustToProcess = (SELECT CustomerID FROM @CustomerList WHERE RowNo = @Counter)
    PRINT @CustToProcess
    SELECT * INTO #RowsForProcessing FROM #SortedRows WHERE CustomerID = @CustToProcess
    UPDATE #Results SET OrderDate1 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 1) WHERE CustomerID = @CustToProcess
    UPDATE #Results SET OrderDate2 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 2) WHERE CustomerID = @CustToProcess
    UPDATE #Results SET OrderDate3 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 3) WHERE CustomerID = @CustToProcess
    UPDATE #Results SET OrderDate4 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 4) WHERE CustomerID = @CustToProcess
    UPDATE #Results SET OrderDate5 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 5) WHERE CustomerID = @CustToProcess
    UPDATE #Results SET OrderDate6 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 6) WHERE CustomerID = @CustToProcess
    UPDATE #Results SET OrderDate7 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 7) WHERE CustomerID = @CustToProcess
    UPDATE #Results SET OrderDate8 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 8) WHERE CustomerID = @CustToProcess
    UPDATE #Results SET OrderDate9 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 9) WHERE CustomerID = @CustToProcess
    UPDATE #Results SET OrderDate10 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 10) WHERE CustomerID = @CustToProcess
    UPDATE #Results SET OrderDate11 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 11) WHERE CustomerID = @CustToProcess
    UPDATE #Results SET OrderDate12 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 12) WHERE CustomerID = @CustToProcess

    DROP Table #RowsForProcessing
END
SELECT * FROM #Results
  

Ответ №3:

Попробуйте это, если вы используете MS SQL Server:

  -- 1st, get the number of columns
declare @columnnumber int = 1
select @columnnumber = max(a.count)
from (
select c.cid,c.name, count(o.orderdate) as count
from 
customer_table c
join order_table o
on c.cid = o.cid
group by c.cid,c.name)a
print @columnnumber

-- Compose the column names for Pivot
declare @columnname varchar(max) = ''
declare @int int = 1
while @int <= @columnnumber
begin
set @columnname = @columnname   '[date'   cast(@int as varchar(10))  '],'
set @int = @int   1
end
set @columnname = '(' left(@columnname,len(@columnname)-1) ')'
print @columnname

--Pivot !!!   Dynamic SQL
declare @str varchar(max)
set @str = 
'SELECT *  
FROM  
(SELECT c.cid,c.name, o.orderdate,concat(''date'',row_number() over (partition by c.cid,c.name order by o.orderdate)) rnk
    FROM customer_table c
    join order_table  o
    on c.cid = o.cid) AS s
PIVOT  
(  
min(s.orderdate)  
FOR s.rnk IN '  @columnname 
' ) AS PivotTable'
print @str

execute (@str)
  

Пожалуйста, измените имя столбца. Я использовал cid в качестве вашего customerid.

Вывод:

 cid name    date1       date2       date3 
12  John    2017-03-04  2017-05-26  2017-12-01
4   Nancy   2017-02-01  NULL        NULL
  

Ответ №4:

Как говорили другие, в принципе невозможно (в чистом SQL) сгенерировать это, не зная, сколько заказов у вас есть для одного клиента.

Мне нравится ответ @nikhil-sugandh, он отлично работает, если вы согласны с тем, что все заказы разделены запятыми в одном столбце.

Если вы настаиваете на наличии нескольких столбцов, вы можете использовать этот ответ, заменив LISTAGG его на ARRAY_AGG и обработав его. Это будет НАМНОГО эффективнее, чем, например, предлагаемое решение с несколькими объединениями. Вы также можете использовать ARRAY_SLICE для обработки случаев, когда заказов больше, чем вы готовы.

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

 create or replace table customer_table(customerId int, name varchar) 
as select * from values
(12,'John'),(4,'Nancy');

create or replace table order_table(orderId int, customerId int, orderDate date) 
as select * from values
(1,12,'3/4/2017'),(2,12,'5/26/2017'),(3,12,'12/1/2017'),(4,4,'2/1/2017'),(5,12,'1/1/2019');

with subq as (
  select c.customerid, name, 
    array_agg(orderdate) within group (order by orderdate) as orders
  from customer_table c 
  inner join order_table o on c.customerid = o.customerid 
  group by c.customerid, c.name
) 
select customerid, name, 
  orders[0]::date AS order1, orders[1]::date AS order2,
  array_to_string(array_slice(orders, 2, 999), ' , ') AS overflow 
from subq;

------------ ------- ------------ ------------ ------------------------- 
 CUSTOMERID | NAME  |   ORDER1   |   ORDER2   |        OVERFLOW         |
------------ ------- ------------ ------------ ------------------------- 
 4          | Nancy | 2017-02-01 | [NULL]     |                         |
 12         | John  | 2017-03-04 | 2017-05-26 | 2017-12-01 , 2019-01-01 |
------------ ------- ------------ ------------ ------------------------- 
  

Комментарии:

1. Можно узнать, сколько заказов для одного клиента. Но проблема в том, что количество заказов меняется со временем, поэтому в этом случае требуется динамический SQL и Pivot. У него также есть причудливое название: динамический свод. Вы можете использовать Google, если хотите узнать об этом больше. Пожалуйста, попробуйте мой ответ.

2. Как я уже сказал — «в чистом SQL» 🙂 С помощью динамического SQL вы можете делать все, что угодно 🙂

Ответ №5:

сначала создайте представление, подобное этому:

 create view order_view as 
select 
count(*) over (partition by customerId order by orderDate) as ord,
CustomerId,
orderdate
 from order_table 
  

затем вы можете использовать этот запрос:

  select c.customerid, 
 o1.orderdate,
 o2.orderdate
 o3.orderdate
 .
 .
 .
 o12.orderdate
from customer_table c 
left join  order_view  o1
     on c.customerid = o1.customerid and ord = 1 
left join  order_view  o2
     on c.customerid = o2.customerid and ord = 2 
left join  order_view  o3
     on c.customerid = o3.customerid and ord = 3
.
.
.     
left join  order_view  o12
     on c.customerid = o12.customerid and ord = 12