SQL Pivot without Aggregation - duplicate entries - sql

I have the following table structure
Create Table PivotTabSample (
ID INT IDENTITY(100000001,1) NOT NULL,
Product Nvarchar(30),
StoreNumber INT,
StoreSalesEstimate DECIMAL,
StoreSalesActual DECIMAL
)
The sample values are populated with the following insert -
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0001','101',500,450)
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0002','101',300,350)
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0003','101',50,61)
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0004','101',100,900)
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0005','101',10,9)
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0001','102',1500,1450)
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0002','102',1400,3500)
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0003','102',150,610)
INSERT INTO [dbo].[PivotTabSample]
([Product],[StoreNumber],[StoreSalesEstimate],[StoreSalesActual])
VALUES ('P0004','102',800,900)
I am trying to do a PIVOT on this data set. and I run the following query -
SELECT Product, [101], [102]
FROM PivotTabSample
PIVOT
( min(StoreSalesActual)
FOR StoreNumber IN ([101],[102])
)AS p
The result that is get is not desired manner - I do not get in one the Product and Store details. There are two Product rows for each Store.
I do not want that. There is no aggregation required here, but I am not sure how to achieve it.
I would like to have the results like this -
Product 101ActualSales 101EstimatedSales 102ActualSales 102estimatedSales
P001 100 101 90 91
P002
P003
P001
Please advise.

WITH CTE
AS (SELECT Product,
ISNULL([101], 0) AS '101ActualSales',
ISNULL([102], 0) AS '102ActualSales',
0 AS '101EstimatedSales',
0 AS '102EstimatedSales'
FROM PivotTabSample
PIVOT
(
SUM(StoreSalesActual)
FOR StoreNumber IN ([101], [102])
) AS p)
SELECT product,
SUM([101ActualSales]) AS [101actualsales],
SUM([101EstimatedSales]) AS [101EstimateSales],
SUM([102ActualSales]) AS [102actualsales],
SUM([102EstimatedSales]) AS [102EstimateSales]
FROM
(
SELECT *
FROM CTE
UNION
SELECT pt.Product,
0 AS '101ActualSales',
0 AS '102ActualSales',
ISNULL([101], 0) AS '101EstimateSales',
ISNULL([102], 0) AS '102EstimateSales'
FROM PivotTabSample
PIVOT
(
SUM(StoreSalesEstimate)
FOR StoreNumber IN ([101], [102])
) AS pt
) AS x
GROUP BY product;
http://rextester.com/GFID38296

Having just had a similar problem at work where I've had to modify a pivot that had the same output as yours, I've found that wrapping into a CTE helped. I just need to figure out why, but I suspect adding the StoreSalesActual has helped...
WITH cte AS
(
SELECT Product,
StoreNumber,
StoreSalesActual
FROM #PivotTabSample
)
SELECT DISTINCT Product, [101], [102]
FROM cte
PIVOT
(
MAX(StoreSalesActual)
FOR StoreNumber IN ([101],[102])
)AS p
GROUP BY Product, [101], [102]

Related

Combining rows and columns in SQL

I am trying to convert my rows into columns, or my columns into rows... I am a little confused with which it is exactly but here's what I would want it to look like
Original table:
Month | Price
1 500
2 600
3 700
what it needs to look like:
1 2 3
500 600 700
Could anyone tell me how his could be done?
EDIT:
CREATE table #yourtable (
[Id] int,
[Value] varchar(6),
[ColumnName] varchar(13)) ;
INSERT INTO #yourtable (
[Id],
[Value],
[ColumnName])
VALUES
(1, '1', 'Month'),
(2, '500', 'Price') ;
select
Month,
Price
from (
select
value,
columnname
from #yourtable ) d
pivot
(max(value) for columnname in (Month, Price) ) piv;
You wrote an almost correct query.
select
Month,
Price
from (
select
value,
columnname
from #yourtable) d
pivot
(max(value) for columnname in ('Month' AS Month, 'Price' AS Price) ) piv;

How can I use Recursive CTE to find all products that were purchased together?

I'm working on a problem that requires to find all possible items that were purchased together in past matching the order at hand. The store has a large selection of items, and the customer could purchase any number of items together (i.e. 1 to N products). I've to find all items that were purchased together (including items purchased alone) in past which matches the current order in terms of items and qty ordered. If exact quantities can't be found then find the closest group.
DECLARE #SampleData as TABLE (OrderID int, Item varchar(20) , PurchaseQty int)
INSERT INTO #SampleData VALUES (1 ,'Shirt' ,2)
INSERT INTO #SampleData VALUES (1 ,'Shoes' ,1)
INSERT INTO #SampleData VALUES (2 ,'Shirt' ,4)
INSERT INTO #SampleData VALUES (2 ,'Pant' ,1)
INSERT INTO #SampleData VALUES (2 ,'Shoes' ,1)
INSERT INTO #SampleData VALUES (3 ,'T-Shirt' ,3)
INSERT INTO #SampleData VALUES (3 ,'Short' ,2)
INSERT INTO #SampleData VALUES (4 ,'Shirt' ,1)
INSERT INTO #SampleData VALUES (5 ,'Shoes' ,3)
INSERT INTO #SampleData VALUES (5 ,'Shirt' ,1)
INSERT INTO #SampleData VALUES (5 ,'Pant' ,1)
INSERT INTO #SampleData VALUES (5 ,'Tie' ,1)
e.g. if the current order is for (2 shirt, 1 shoes), I'like to find all orders that have either of these or both in it. So from above data, the result should be like OrderId 1,2,5 and 4 since they have either or both of these items. Notice the sequence of orders is to match the exact and then the next closet.
I've tried following recursive CTE, but it is entering endless recursion and crashing:
;WITH temp AS
(
SELECT sd.OrderID, sd.Item, sd.PurchaseQty
FROM #SampleData sd
WHERE sd.Item in ('shirt','short')
UNION ALL
SELECT sd.OrderID, t.Item, sd.PurchaseQty
FROM #SampleData sd
INNER JOIN temp t ON t.item = sd.Item
)
SELECT * FROM temp t
Order By t.PurchaseQty
Althogh in this case, I've selected (shirt,shoes) as a possible order, customer can order any number of items in any qty, e.g. an order of 35 distinct items. I've to find either an order having the exact match in terms of items and qty, or the closet match. e.g. if it can't find an order with 2 shirts and 1 shoe, find an order with 1 shirt and 1 shoe etc.
Is recursive CTE the correct type of query for such a problem, if yes, how can I achieve it.
No, you don't need a recursive query. A simple self-join with EXISTS will work:
select * from #SampleData s
where exists
( select * from #SampleData q
where q.OrderId = s.OrderID
and q.Item in ('Shirt','Shoes') )
If the number of items in the current order is large, you may want to put the current order in a temp table ##CurrentOrder and join to it in the inner query:
select * from #SampleData s
where exists
( select * from #SampleData q
where q.OrderId = s.OrderID
and q.Item in (select Item from ##CurrentOrder) )
You can use STRING_AGG with the Distinct values.
the SElection of the order happens in the subselect, as STRINg_AGG doesn't support DISTINCT
CREATE tABLE SampleData (OrderID int, Item varchar(20) , PurchaseQty int)
INSERT INTO SampleData VALUES (1 ,'Shirt' ,2)
INSERT INTO SampleData VALUES (1 ,'Shoes' ,1)
INSERT INTO SampleData VALUES (2 ,'Shirt' ,4)
INSERT INTO SampleData VALUES (2 ,'Pant' ,1)
INSERT INTO SampleData VALUES (2 ,'Shoes' ,1)
INSERT INTO SampleData VALUES (3 ,'T-Shirt' ,3)
INSERT INTO SampleData VALUES (3 ,'Short' ,2)
INSERT INTO SampleData VALUES (4 ,'Shirt' ,1)
INSERT INTO SampleData VALUES (5 ,'Shoes' ,1)
INSERT INTO SampleData VALUES (5 ,'Pant' ,1)
INSERT INTO SampleData VALUES (5 ,'Tie' ,1)
GO
SELECT
STRING_AGG(orderID,',')
WITHIN GROUP ( ORDER BY OrderID ASC )
FROM
(SELECT DISTINCT orderID FROM SampleData WHERE Item IN ('shirt', 'shoes')) t1
GO
| (No column name) |
| :--------------- |
| 1,2,4,5 |
db<>fiddle here

Insert grouped data

I am getting expected results from my query, I am using group by to group the data on the basis of different Ids.
The problem I am facing is that I have to insert this grouped data in the table called gstl_calculated_daily_fee, but when I pass the grouped result to variables called #total_mada_local_switch_high_value and #mada_range_id and insert them in the table then I get only the last result of the query in the table.
Sample result:
Fee range_id
1.23 1
1.22 2
2.33 3
I get only 2.33 and 1 after I insert but I have to insert the whole result in to the table.
Please suggest how can I insert the whole query result into the table. Below is the query:
DECLARE #total_mada_local_switch_high_value decimal(32,4) = 0.00;
DECLARE #mada_range_id int = 0;
select
#total_mada_local_switch_high_value = SUM(C.settlement_fees),
#mada_range_id = C.range_id
From
(
select
*
from
(
select
rowNumber = #previous_mada_switch_fee_volume_based_count + (ROW_NUMBER() OVER(PARTITION BY DATEPART(MONTH, x_datetime) ORDER BY x_datetime)),
tt.x_datetime
from gstl_trans_temp tt where (message_type_mapping = 0220) and card_type ='GEIDP1' and response_code IN(00,10,11) and tran_amount_req >= 5000
) A
CROSS APPLY
(
select
rtt.settlement_fees,
rtt.range_id
From gstl_mada_local_switch_fee_volume_based rtt
where A.rowNumber >= rtt.range_start
AND (A.rowNumber <= rtt.range_end OR rtt.range_end IS NULL)
) B
) C
group by CAST(C.x_datetime AS DATE),C.range_id
-- Insert Daily Volume
INSERT INTO
gstl_calculated_daily_fee(business_date,fee_type,fee_total,range_id)
VALUES
(#tlf_business_date,'MADA_SWITCH_FEE_LOCAL_CARD', #total_mada_local_switch_high_value, #mada_range_id)
I see no need for variables here. You can insert the aggregated results directly.
Sample data
create table Data
(
Range int,
Fee money
);
insert into Data (Range, Fee) values
(1, 1.00),
(1, 0.50),
(2, 3.00),
(3, 0.25),
(3, 0.50);
create table DataSum
(
Range int,
FeeSum money
);
Solution
insert into DataSum (Range, FeeSum)
select d.Range, sum(d.Fee)
from Data d
group by d.Range;
Fiddle to see things in action.

using row_number to return specific rows of query

I am using SQL Server 2012 & MATLAB. I have a table of 5 columns (1 char, 1 datetime and 3 floats). I have a simple query shown below that returns the data from this table which contains over a million records - this however causes an out of memory error in MATLAB.
simple query
select id_co, date_r, FAM_Score, FAM_A_Score, FAM_Score
from GSI_Scores
where id_co <> 'NULL'
order by id_co, date_rating
So I was looking to breakdown the query select the data in batches of 250,000 records. I have just come across the ROW_NUMBER function which I added to my query, please see below. This numbers all the records for me. However I am having trouble selecting say records between 250,000 and 500,000. How do I do this?
updated query
select id_co, date_r, FAM_Score, FAM_A_Score, FAM_Score, row_number() over (order by id_co) as num_co
from GSI_Scores
where id_co <> 'NULL' and num_sedol between 250000 and 500000
order by id_co, date_rating
Simply use a sub query or Common Table Expression (CTE).
;WITH CTE AS
(
--Your query
)
SELECT * FROM CTE
WHERE num_co BETWEEN 250000 AND 500000
Just an sample example
declare #t table (ID INT)
insert into #t (id)values (1)
insert into #t (id)values (2)
insert into #t (id)values (3)
insert into #t (id)values (4)
;WITH CTE AS
(
select *,COUNT(ID)OVER (PARTITION BY ID ) RN from #t
)
Select ID from CTE C WHERE C.ID BETWEEN 2 AND 4
ORDER BY RN
OR
;WITH CTE (select id_co,
date_r,
FAM_Score,
FAM_A_Score,
FAM_Score,
COUNT(id_co) over (PARTITION BY ID DESC) as num_co
from GSI_Scores
where id_co <> 'NULL')
Select C.d_co,
C.date_r,
C.FAM_Score,
C.FAM_A_Score,
C.FAM_Score FROM CTE C
WHERE C.id_co between 250000 and 500000
order by C.id_co, C.date_rating
You could try using the OFFSET x ROWS FETCH NEXT y ROWS ONLY commands like this:
CREATE TABLE TempTable (
TempID INT IDENTITY(1,1) NOT NULL,
SomeDescription VARCHAR(255) NOT NULL,
PRIMARY KEY(TempID))
INSERT INTO TempTable (SomeDescription)
VALUES ('Description 1'),
('Description 2'),
('Description 3'),
('Description 4'),
('Description 5'),
('Description 6'),
('Description 7'),
('Description 8'),
('Description 9'),
('Description 10')
SELECT * FROM TempTable ORDER BY TempID OFFSET 3 ROWS FETCH NEXT 2 ROWS ONLY;

SQL Server Simple Group by query

I have a simple problem , Although i believe its simple , am not able to figure out the same.
Consider i have the below table with exactly same data as given below :
CREATE TABLE #temp
(
link varchar(255),
number INT,
fname varchar(255)
)
insert into #temp VALUES ('abc',1,'f1')
insert into #temp VALUES ('abc',2,'f2')
insert into #temp VALUES ('abc',3,'f3')
insert into #temp VALUES ('abc',4,'f6')
insert into #temp VALUES ('abc',10,'f100')
insert into #temp VALUES ('abe',-1,'f0')
insert into #temp VALUES ('abe',1,'f1')
insert into #temp VALUES ('abe',2,'f2')
insert into #temp VALUES ('abe',3,'f3')
insert into #temp VALUES ('abe',4,'f6')
insert into #temp VALUES ('abe',20,'f200')
insert into #temp VALUES ('cbe',-1,'f0')
insert into #temp VALUES ('cbe',1,'f1')
insert into #temp VALUES ('cbe',2,'f2')
insert into #temp VALUES ('cbe',3,'f3')
Now for a given link , i need to get the max 'number' and the corresponding 'fname' which has the max 'number' for the given 'link'.
1)Ex : if link is 'abc' , output should be
abc, 10, f100
2)Ex : if link if 'abe' , Output should be
abe, 20, f200
3)Now link can be also given as a pattern , like (link like 'ab%') , so output should be
abc, 10, f100
abe, 20, f200
4)if (link like 'cb%') , so output should be
cbe, 3, f3
Any help in writing this group by query. I have a solution using CAST and string concat like below , but that seems to be in-efficient.
select link,number,fname from #temp
where link like 'ab%' and link+'_'+CAST(number AS varchar(255))
in (select link+'_'+CAST(MAX(number) AS varchar(255)) from #temp
group by link)
Thanks..
Using a self join:
SELECT x.link,
x.number,
x.fname
FROM #temp x
JOIN (SELECT t.link,
MAX(t.number) AS max_number
FROM #temp t
GROUP BY t.link) y ON y.link = x.link
AND y.max_number = x.number
Using a CTE and ROW_NUMBER (SQL Server 2005+):
WITH cte AS (
SELECT x.link,
x.number,
x.fname,
ROW_NUMBER() OVER(PARTITION BY x.link
ORDER BY x.number DESC) rank
FROM #temp x)
SELECT c.link,
c.number,
c.fname
FROM cte c
WHERE c.rank = 1