Table structure issue - Query Pivot/Unpivot - sql

I am trying to query a table for data conversion but I am running into issues with the structure. I am guessing I need to Pivot then Unpivot but I am not sure where to begin.
This is my current table
+----+-----------+-------+---------+
| ID | field | value | date |
+----+-----------+-------+---------+
| 1 | Draw1 | 1500 | NULL |
| 1 | Draw1Date | NULL | 4/15/16 |
| 1 | Draw1Fee | 100 | NULL |
| 1 | Draw2 | 2000 | NULL |
| 1 | Draw2Date | NULL | 3/14/17 |
| 1 | Draw2Fee | 100 | NULL |
| 2 | Draw1 | 800 | NULL |
| 2 | Draw1Date | NULL | 4/16/18 |
| 2 | Draw1Fee | 150 | NULL |
| 2 | Draw2 | 760 | NULL |
| 2 | Draw2Date | NULL | 5/6/18 |
| 2 | Draw2Fee | 150 | NULL |
+----+-----------+-------+---------+
Result needed
+----+-------+---------+---------+------+
| ID | Draws | Amount | Date | Fee |
+----+-------+---------+---------+------+
| 1 | Draw1 | 1500 | 4/15/16 | 100 |
| 1 | Draw2 | 2000 | 3/14/17 | 100 |
| 2 | Draw1 | 800 | 4/16/18 | 150 |
| 2 | Draw2 | 760 | 5/6/18 | 150 |
+----+-------+---------+---------+------+

My answer works for provided data. if you are looking for more general solution, for more variety of data, you may try to find another answer. I've not used PIVOT/UNPIVOT.
Test Data:
create table #t (ID int, field varchar(20), [value] int, [date] date)
insert into #t values
(1 ,'Draw1' , 1500 , NULL ),
(1 ,'Draw1Date' , NULL , '4/15/16'),
(1 ,'Draw1Fee' , 100 , NULL ),
(1 ,'Draw2' , 2000 , NULL ),
(1 ,'Draw2Date' , NULL , '3/14/17'),
(1 ,'Draw2Fee' , 100 , NULL ),
(2 ,'Draw1' , 800 , NULL ),
(2 ,'Draw1Date' , NULL , '4/16/18'),
(2 ,'Draw1Fee' , 150 , NULL ),
(2 ,'Draw2' , 760 , NULL ),
(2 ,'Draw2Date' , NULL , '5/6/18' ),
(2 ,'Draw2Fee' , 150 , NULL )
Query:
;with ct as (
select ID, field
from #t
where field in ('Draw1', 'Draw2')
group by ID, field
)
select ct.ID, ct.field
, t1.[value] as Amount, t2.[date] as [Date], t3.[value] as Fee
from ct
inner join #t t1 on t1.ID = ct.ID and t1.field = ct.field
inner join #t t2 on t2.ID = ct.ID and t2.field = ct.field + 'Date'
inner join #t t3 on t3.ID = ct.ID and t3.field = ct.field + 'Fee'
Result:
ID field Amount Date Fee
1 Draw1 1500 2016-04-15 100
1 Draw2 2000 2017-03-14 100
2 Draw1 800 2018-04-16 150
2 Draw2 760 2018-05-06 150

Try this...
SELECT tblAmount.id AS ID,
tblAmount.field AS Draws,
Max(tblAmount.value) AS Amount,
Max(tblDate.[date]) AS [Date],
Max(tblFee.value) AS Fee
FROM tablename tblAmount
INNER JOIN (SELECT id, field, [date]
FROM tablename
WHERE [date] IS NOT NULL AND field LIKE '%Date') tblDate
ON tblAmount.id = tblDate.id
AND tblDate.field LIKE tblAmount.field + '%'
INNER JOIN (SELECT id, field, value
FROM tablename
WHERE value IS NOT NULL AND field LIKE '%Fee') tblFee
ON tblAmount.id = tblFee.id
WHERE tblAmount.value IS NOT NULL
AND tblAmount.field NOT LIKE '%Fee'
AND tblAmount.field NOT LIKE '%Date'
GROUP BY tblAmount.id, tblAmount.field
ORDER BY tblAmount.id, tblAmount.field
Output
+----+-------+--------+------------+-----+
| ID | Draws | Amount | Date | Fee |
+----+-------+--------+------------+-----+
| 1 | Draw1 | 1500 | 2016-04-15 | 100 |
| 1 | Draw2 | 2000 | 2017-03-14 | 100 |
| 2 | Draw1 | 800 | 2018-04-16 | 150 |
| 2 | Draw2 | 760 | 2018-05-06 | 150 |
+----+-------+--------+------------+-----+
Demo: http://www.sqlfiddle.com/#!18/97688/101/0

I would simply do:
select id, left(field, 5),
max(case when len(field) = 5 then value end) as value,
max(case when field like '%date' then value end) as date,
sum(case when field like '%fee' then value end) as fee
from t
group by id, left(field, 5);
If your field is really more complex, do you are looking for date and fee at the end, by anything before, then use cross apply:
select t.id, v.draws,
max(case when t.field = v.draws then value end) as value,
max(case when t.field like '%date' then value end) as date,
sum(case when t.field like '%fee' then value end) as fee
from t cross apply
(values (replace(replace(field, 'date', ''), 'fee', '')) v(draws)
group by id, v.draws;

Related

How to show values that has a suitable pair in sql server

I have a table like this
---------------------------------------------
Id | TransactionId | Amount | Account| crdr |
---------------------------------------------
1 | 1 | 100 | 11111 | 1 |
2 | 2 | 130 | 13133 | 1 |
3 | 1 | 100 | 12111 | 2 |
4 | 2 | 130 | 13233 | 2 |
5 | 2 | 110 | 12122 | 1 |
What I need to display is, show these records as pairs (I have grouped them by transactionid, Amount).
SELECT TransactionId ,Amount , Account, CrDr
FROM Table1 ORDER BY TransactionId ASC,Amount ASC, CrDr ASC
But I want to ignore the records which dont have a pair, as a Example for this above records set result should be like this
---------------------------------------------
TransactionId | Amount | Account| crdr |
---------------------------------------------
1 | 100 | 11111 | 1 |
1 | 100 | 12111 | 2 |
2 | 130 | 13133 | 1 |
2 | 130 | 13233 | 2 |
Can someone suggest a solution for this.
You could use a correlated subquery with a NOT EXISTS condition to ensure that another record exists with the same TransactionId and Amount:
SELECT TransactionId ,Amount , Account, CrDr
FROM Table1 t
WHERE EXISTS (
SELECT 1
FROM Table1 t1
WHERE
t.id <> t1.id
AND t.TransactionId = t1.TransactionId
AND t.Amount = t1.Amount
)
ORDER BY TransactionId ASC,Amount ASC, CrDr ASC
Demo on DB Fiddle:
TransactionId | Amount | Account | CrDr
------------: | -----: | ------: | ---:
1 | 100 | 11111 | 1
1 | 100 | 12111 | 2
2 | 130 | 13133 | 1
2 | 130 | 13233 | 2
Try this:
DECLARE #DataSource TABLE
(
[Id] INT
,[TransactionId] INT
,[Amount] INT
,[Account] INT
,[crdr] INT
);
INSERT INTO #DataSource ([Id], [TransactionId], [Amount], [Account], [crdr])
VALUES (1, 1, 100, 11111, 1)
,(2, 2, 130, 13133, 1)
,(3, 1, 100, 12111, 2)
,(4, 2, 130, 13233, 2)
,(5, 2, 110, 12122, 1);
WITH DataSource AS
(
SELECT *
,COUNT(*) OVER (PARTITION BY [TransactionId], [Amount]) AS [Count]
FROM #DataSource
)
SELECT *
FROM DataSource
WHERE [Count] = 2
ORDER BY TransactionId ASC,Amount ASC, CrDr ASC;
Try this: This this simplest solution of this problem
;with cte
as
(
select TransactionId, Amount
from Table1
group by TransactionId, Amount
having count(*) > 1
)
select *
from Table1 t
inner join cte c on t.TransactionId = c.TransactionId and t.Amount = c.Amount

SQL Combining multiple rows into one

I want to merge multiple rows into one, and only keep the values where the value is not NULL
Here is what i want to achieve:
I want from this
+----+-----------------+-----------------+-----------------+--------------------+
| ID | 1stNofification | 2ndNotification | 3rdNotification | NotificationNumber |
+----+-----------------+-----------------+-----------------+--------------------+
| 1 | 01.01.2019 | NULL | NULL | 1 |
+----+-----------------+-----------------+-----------------+--------------------+
| 1 | NULL | 02.02.2019 | NULL | 2 |
+----+-----------------+-----------------+-----------------+--------------------+
| 1 | NULL | NULL | 03.03.2019 | 3 |
+----+-----------------+-----------------+-----------------+--------------------+
| 2 | 06.01.2019 | NULL | NULL | 1 |
+----+-----------------+-----------------+-----------------+--------------------+
| 2 | NULL | 09.02.2019 | NULL | 2 |
+----+-----------------+-----------------+-----------------+--------------------+
| 2 | NULL | NULL | 11.03.2019 | 3 |
+----+-----------------+-----------------+-----------------+--------------------+
to this:
+----+-----------------+-----------------+-----------------+
| ID | 1stNofification | 2ndNotification | 3rdNotification |
+----+-----------------+-----------------+-----------------+
| 1 | 01.01.2019 | 02.02.2019 | 03.03.2019 |
+----+-----------------+-----------------+-----------------+
| 2 | 06.01.2019 | 09.02.2019 | 11.03.2019 |
+----+-----------------+-----------------+-----------------+
I tried something like:
SELECT
ID,
MAX(CASE WHEN a.NotificationNumber = 1 THEN 1stNotification END)1stNotification,
MAX(CASE WHEN a.NotificationNumber = 2 THEN 2ndNotification END)2ndNotification,
MAX(CASE WHEN a.NotificationNumber = 3 THEN 3rdNotification END)3rdNotification
FROM Notifications
GROUP BY ID
But that did not give me my expected results unfortunately.
Would really appreciate if someone could help me out :)
You just need to use max without any case
SELECT
ID,
MAX(1stNotification) AS 1stNotification,
MAX(2ndNotification) AS 2ndNotification,
MAX(3rdNotification) AS 3rdNotification
FROM Notifications
GROUP BY ID
I think you need something like this...
; with cte as (
select 1 as id, 'dd' as not1, null as not2, null as not3 , 1 as notifications
union all
select 1, null, 'df', null , 2
union all
select 1, null, null, 'vc', 3
union all
select 2, 'ws', null, null, 1
union all
select 2, null, 'xs', null, 2
union all
select 2, null, null, 'nm', 3
)
, ct as (
select id, coalesce(not1, not2, not3) as ol, notifications ,
'notification' + cast(notifications as varchar(5)) as Col
from cte
)
select * from (
select id, ol, col from ct )
as d
pivot (
max(ol) for col in ( [notification1], [notification2], [notification3] )
) as P
Here as per my understanding your notification columns in result are actually notification number mention in rows.

SQL Server : display distinct list of orders, and indicate which orders contain specific products

I have a table that contains a lot of data, but the relevant data in the table looks something like this:
Orders table:
+----------+-----------+---------------+
| OrderID | Product | Date |
+----------+-----------+---------------+
| 1 | Apple | 01/01/2001 |
| 1 | Pear | 01/01/2001 |
| 1 | Pear | 01/01/2001 |
| 1 | Orange | 01/01/2001 |
| 1 | Pineapple | 01/01/2001 |
| 2 | Cherry | 02/02/2002 |
| 2 | Cherry | 02/02/2002 |
| 3 | Orange | 03/03/2003 |
| 3 | Apple | 03/03/2003 |
| 3 | Cherry | 03/03/2003 |
+----------+-----------+---------------+
I'd like a query to return a distinct list of orders, and if the order contains certain products, to indicate as such:
+----------+-----------+--------+-------+
| OrderID | Date | Apple? | Pear? |
+----------+-----------+--------+-------+
| 1 |01/01/2001 | X | X |
| 2 |02/02/2002 | | |
| 3 |03/03/2003 | X | |
+----------+-----------+--------+-------+
Here's where I've left off and decided to seek out help:
WITH CTEOrder AS
(
SELECT
OrderID, Product, Date,
ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY OrderID ASC) AS OrderRN
FROM
Orders
)
CTEApple as
(
SELECT
OrderID, Product, Date,
ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY OrderID ASC) AS AppleRN
FROM
Orders
WHERE
Product = 'Apple'
),
CTEPear
(
SELECT
OrderID, Product, Date,
ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY OrderID ASC) AS PearRN
FROM
Orders
WHERE
Product = 'Pear'
)
SELECT
o.OrderID, o.Product, o.Date,
co.OrderRN, a.AppleRN, p.PearRN
FROM
Orders AS o
OUTER JOIN
CTEOrder AS co ON o.OrderID = co.Orderid
OUTER JOIN
CTEApple AS a ON o.OrderID = a.OrderID
OUTER JOIN
CTEPear AS p ON o.OrderID = p.OrderID
WHERE
(co.OrderRN IS NULL AND a.AppleRN IS NULL AND p.PearRN IS NULL
OR co.OrderRN = 1 AND a.AppleRN IS NULL AND p.PearRN IS NULL
OR co.OrderRN = 1 AND a.AppleRN = 1 AND p.PearRN IS NULL
OR co.OrderRN = 1 AND a.AppleRN = 1 AND p.PearRN = 1
OR co.OrderRN = 1 AND a.AppleRN IS NULL AND p.PearRN = 1
OR co.OrderRN IS NULL AND a.AppleRN = 1 AND p.PearRN IS NULL
OR co.OrderRN IS NULL AND a.AppleRN = 1 AND p.PearRN = 1
OR co.OrderRN IS NULL AND a.AppleRN IS NULL AND p.PearRN = 1)
Currently my result set is unwieldy with a significant amount of duplication.
I'm thinking that I am heading in the wrong direction, but I don't know what other tools are available to me within SQL Server to cut up this data the way I need.
Thanks for any guidance!
Here's my result set after Nik Shenoy's guidance:
+----------+-----------+----------------+
| OrderID | Date | Apple? | Pear? |
+----------+-----------+----------------+
| 1 | 01/01/2001| x | NULL |
| 1 | 01/01/2001| NULL | x |
| 1 | 01/01/2001| NULL | x |
| 1 | 01/01/2001| NULL | NULL |
| 1 | 01/01/2001| NULL | NULL |
| 2 | 02/02/2002| NULL | NULL |
| 2 | 02/02/2002| NULL | NULL |
| 3 | 03/03/2003| NULL | NULL |
| 3 | 03/03/2003| x | NULL |
| 3 | 03/03/2003| NULL | NULL |
+----------+-----------+----------------+
What is my next step to have only 1 row per Order:
+----------+-----------+--------+-------+
| OrderID | Date | Apple? | Pear? |
+----------+-----------+--------+-------+
| 1 |01/01/2001 | X | X |
| 2 |02/02/2002 | | |
| 3 |03/03/2003 | X | |
+----------+-----------+--------+-------+
You can just use conditional aggregation:
select o.orderid, date,
max(case when product = 'Apple' then 'X' end) as IsApple,
max(case when product = 'Pear' then 'X' end) as IsPear
from orders o
group by o.orderid, date;
If you know all the products in advance, you can use the Transact-SQL PIVOT relational operator to cross-tabulate the data by product. If you use MAX or COUNT, you can just transform non-NULL or non-ZERO output to an 'x'
SELECT
PivotData.OrderID
, PivotData.OrderDate
, CASE WHEN PivotData.Apple IS NULL THEN '' ELSE 'X' END AS [Apple?]
, CASE WHEN PivotData.Pear IS NULL THEN '' ELSE 'X' END AS [Pear?]
, CASE WHEN PivotData.Orange IS NULL THEN '' ELSE 'X' END AS [Orange?]
, CASE WHEN PivotData.Pineapple IS NULL THEN '' ELSE 'X' END AS [Pineapple?]
, CASE WHEN PivotData.Cherry IS NULL THEN '' ELSE 'X' END AS [Cherry?]
FROM
(SELECT OrderID, Product, OrderDate) AS [Order]
PIVOT (MAX(Product) FOR Product IN ( [Apple], [Pear], [Orange], [Pineapple], [Cherry] )) AS PivotData

SUM values in last row

I have table with values
+--------+------------+-------------+
| XPK | Money | NumOfDevices|
+--------+------------+-------------+
| 1 | 1000 | 2 |
| 2 | 2000 | 3 |
| 3 | 3000 | 4 |
+--------+------------+-------------+
Need to sum all values and to enter the asterisk "*" in entire row to separate TOTAL Values from other values, so result need to look something like this
+--------+------------+-------------+
| XPK | Money | NumOfDevice |
+--------+------------+-------------+
| 1 | 1000 | 2 |
| 2 | 2000 | 3 |
| 3 | 3000 | 4 |
|***********************************|
| TOTAL | 6000 | 9 |
+--------+------------+-------------+
Any idea ?
The easiest way for this is to use a UNION selecting the totals from the table:
Select Convert(Varchar (10), XPK) XPK,
Money,
NumOfDevices
From YourTable
Union
Select 'TOTAL' As XPK,
Sum(Money),
Sum(NumOfDevices)
From YourTable
Order By Case When XPK = 'TOTAL' Then 1 Else 0 End, XPK
Another method of doing this would be to use a GROUP BY WITH ROLLUP
Select Case When Grouping(XPK) = 1
Then 'TOTAL'
Else Convert(Varchar (10), XPK)
End As XPK,
Sum(Money) Money,
Sum(NumOfDevices) NumOfDevices
From YourTable
Group By XPK With Rollup
Order By Grouping(XPK)
IN Table ERP System SAP
select B.ItemCode as'Item No.',B.Dscription as'Item Description',B.Quantity,B.Price as'Sale Amt',B.LineTotal as'Total'
from ODLN A
inner join DLN1 B On A.DocEntry=B.DocEntry
union all
select 'Total',convert(nvarchar(10),sum(case when b.Dscription is not null then 1 else 0 end)),sum(b.quantity),sum(b.price),sum(b.lineTotal)
from ODLN A
inner join DLN1 B On A.DocEntry=B.DocEntry

How to merge two different rows(how to assign different value is zero)

I am trying to use union for merging two output but these rows value are different.I need different rows value are zero.like output(third) table.I was struggle with pass two days please help me.
Select t1.round,
t1.SC,
t1.ST,
t1.OTHERS,
t2.round_up,
t2.SC_up,
t2.ST_up,
t2.OTHERS_up
From
(Select round as round,
Sum (non_slsc_qty) as SC,
Sum (non_slst_qty) as ST,
Sum (non_slot_qty) as OTHERS
FROM vhn_issue
where (date between '2015-08-01' and '2015-08-31')AND
dvn_cd='15' AND phc_cd='012' AND hsc_cd='05' GROUP BY round) t1
,
(Select round as round_up,
Sum (non_slsc_qty) as SC_up,
Sum (non_slst_qty) as ST_up,
Sum (non_slot_qty) as OTHERS_up,
FROM vhn_issue
where (date between '2015-04-01' and '2015-08-31')AND
dvn_cd='15' AND phc_cd='012' AND hsc_cd='05' GROUP BY round) t2
This first table result
+-----------------------------------+------------+--------+--------
| round | SC | ST | OTHERS |
+-----------------------------------+------------+--------+--------
| 1 | 20 | 30 | 50 |
| | | | |
| | | | |
+-----------------------------------+------------+--------+--------+
This is second table result
+-----------------------------------+------------+--------+----------
| round_up | SC_up | ST_up | OTHERS_up |
+-----------------------------------+------------+--------+-----------
| 1 | 21 | 31 | 51 |
| 3 | 10 | 5 | 2 |
| | | | |
+-----------------------------------+------------+--------+--------+---
I need output like this
+------------+--------+----------------------------------------------
| round_up | SC | ST |OTHERS | SC_up | ST_up |OTHERS_up |
+------------+--------+-----------------------------------------------
| 1 | 20 | 30 | 50 | 21 | 31 | 51 |
| | | | | | | |
| 3 | 0 | 0 | 0 | 10 | 5 | 2 |
+------------+--------+--------+---------------------------------------
You can use WITH Queries (Common Table Expressions) to wrap the two selects and use RIGHT JOIN to get the desired output,COALESCE is used to print 0 instead of NULL.
WITH a
AS (
SELECT round AS round
,Sum(non_slsc_qty) AS SC
,Sum(non_slst_qty) AS ST
,Sum(non_slot_qty) AS OTHERS
FROM vhn_issue
WHERE (
DATE BETWEEN '2015-08-01'
AND '2015-08-31'
)
AND dvn_cd = '15'
AND phc_cd = '012'
AND hsc_cd = '05'
GROUP BY round
)
,b
AS (
SELECT round AS round_up
,Sum(non_slsc_qty) AS SC_up
,Sum(non_slst_qty) AS ST_up
,Sum(non_slot_qty) AS OTHERS_up
,
FROM vhn_issue
WHERE (
DATE BETWEEN '2015-04-01'
AND '2015-08-31'
)
AND dvn_cd = '15'
AND phc_cd = '012'
AND hsc_cd = '05'
GROUP BY round
)
SELECT coalesce(b.round_up, 0) round_up
,coalesce(a.sc, 0) sc
,coalesce(a.st, 0) st
,coalesce(a.others, 0) others
,coalesce(b.sc_up, 0) sc_up
,coalesce(b.st_up, 0) st_up
,coalesce(b.others_up, 0) others_up
FROM a
RIGHT JOIN b ON a.round = b.round_up
WITH Results_CTE AS
(
Select t1.round as round_up ,
t1.SC as SC,
t1.ST as ST,
t1.OTHERS as OTHERS,
0 as SC_up,
0 as ST_up,
0 as OTHERS_up
from round t1
union all
t2.round_up as round_up ,
0 as SC,
0 as ST,
0 as OTHERS,
t2.SC_up,
t2.ST_up,
t2.OTHERS_up from round t2
)
select round_up , sum(SC) as SC,sum (ST) as ST, sum(OTHERS) as OTHERS, sum(SC_up) as SC_up, sum(ST_up) as ST_up, sum(OTHERS_up) as OTHERS_ up
from Results_CTE group by round_up