Unpivot (or alternative) all columns into rows - sql

I have the following data:
create table test(c1 number,
c2 number,
c3 number,
c4 number,
c5 number);
insert into test (c1,c2,c3,c4,c5) values(2000,1844054,50.03,922030,25.01);
insert into test (c1,c2,c3,c4,c5) values(2001,1850861,49.86,937391,25.25);
insert into test (c1,c2,c3,c4,c5) values(2002,1841519,50.32,907995,24.81);
insert into test (c1,c2,c3,c4,c5) values(2003,1804163,49.84,902838,24.94);
I need to translate the data from:
c1 | c2 | c3 | c4 | c5
2000 | 1844054 | 50.03 | 922030 | 25.01
2001 | 1850861 | 49.86 | 937391 | 25.25
2002 | 1841519 | 50.32 | 907995 | 24.81
2003 | 1804163 | 49.84 | 902838 | 24.94
to:
c1 | 2000 | 2001 | 2002 | 2003
c2 | 1844054 | 1850861 | 1841519 | 1804163
c3 | 50.03 | 49.86 | 50.32 | 49.84
c4 | 922030 | 937391 | 907995 | 902838
c5 | 25.01 | 25.25 | 24.81 | 24.94
I didn't succeed with a regular PIVOT, so I ask you for help, thank you.

You are transposing, not just pivoting. You need to UNPIVOT, then PIVOT.
select * from test
unpivot(c for col in(c2,c3,c4,c5))
pivot(max(c) for c1 in(2000,2001,2002,2003))
order by col;
CO 2000 2001 2002 2003
-- ---------- ---------- ---------- ----------
C2 1844054 1850861 1841519 1804163
C3 50.03 49.86 50.32 49.84
C4 922030 937391 907995 902838
C5 25.01 25.25 24.81 24.94

This is a pain. You can unpivot and the re-aggregate. Here is one method:
select which,
max(case when year = 2000 then c end) as val_2000,
max(case when year = 2001 then c end) as val_2001,
max(case when year = 2002 then c end) as val_2002,
max(case when year = 2003 then c end) as val_2003
from ((select c1 as year, 'c2' as which, c2 as c from test) union all
(select c1 as year, 'c3' as which, c3 as c from test) union all
(select c1 as year, 'c4' as which, c4 as c from test) union all
(select c1 as year, 'c5' as which, c5 as c from test)
) x
group by which
order by which;
Here is a db<>fiddle.

SELECT * FROM test
unPIVOT(
quantity -- unpivot_clause
FOR product_code -- unpivot_for_clause
IN ( c2,c3,c4,c5))
pivot(max(quantity) for
c1 in(2000,2001,2002,2003))
order by product_code;

Related

count on one column and group by another column

Suppose that we have a sample table
c1 | c2
--------------
a | b
a | b
a | c
a | c
a | c
d | e
d | e
How can we turn this table into the following format
c1 | c2 | c3
--------------------------
a | b | 2
a | c | 3
d | e | 2
where c3 contains the count of c2 based on distinct c1 value.
This is a group by with two keys:
select c1, c2, count(*)
from t
group by c1, c2;
select c1, c2, count(*) as c3
from your_table
group by c1, c2

How to get a fixed number of rows in sql

If I have 4 rows in database like these.
|____A____|____B_____|
| a1 | b1 |
| a2 | b2 |
| a3 | b3 |
| a4 | b4 |
But I need to display 10 rows by added NO column to get serial number for each row like these
__NO__|____A____|____B_____|
1 | a1 | b1 |
2 | a2 | b2 |
3 | a3 | b3 |
4 | a4 | b4 |
5 | | |
6 | | |
7 | | |
8 | | |
9 | | |
10 | | |
How to query by sql server?
Fiddle here : http://sqlfiddle.com/#!3/9a9dd/1
WITH CTE1
AS
(
SELECT 1 AS [NO]
UNION ALL
SELECT [NO]+1 FROM CTE1 WHERE [NO]<10
),
CTE2 AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS RN, A,B
FROM YOURTABLE
)
SELECT C1.[NO],A,B
FROM CTE1 C1 LEFT JOIN CTE2 C2 ON C1.[NO] = C2.RN
Here is another way without using recursive cte and LEFT JOIN.
SQL Fiddle
;WITH Cte AS(
SELECT
NO = ROW_NUMBER() OVER (ORDER BY A, B), A, B
FROM tbl
UNION ALL
SELECT
t.n, NULL, NULL
FROM (VALUES
(11), (12), (13), (14), (15), (16), (17), (18), (19), (20)
)t(n)
),
CteFinal AS(
SELECT *, rn = ROW_NUMBER() OVER (ORDER BY NO)
FROM Cte
)
SELECT
rn AS NO, A, B
FROM CteFinal
WHERE rn < = 10

TSQL query based on IF?

This the sample of my data:
+----+---+----+----+----+----+----+-------------+------------+-------------+
| ID | C | C1 | C2 | C3 | C4 | C5 | EndingPoint | TransferID | Transferred |
+----+---+----+----+----+----+----+-------------+------------+-------------+
| 1 | A | A | | | | | B | 1 | 80 |
+----+---+----+----+----+----+----+-------------+------------+-------------+
| 2 | A | A | B | | | | C | 2 | 40 |
+----+---+----+----+----+----+----+-------------+------------+-------------+
| 3 | A | A | B | C | | | A | 3 | 10 |
+----+---+----+----+----+----+----+-------------+------------+-------------+
| 4 | B | B | | | | | C | 1 | 25 |
+----+---+----+----+----+----+----+-------------+------------+-------------+
| 5 | B | B | C | | | | A | 2 | 30 |
+----+---+----+----+----+----+----+-------------+------------+-------------+
| 6 | C | C | | | | | A | 1 | 70 |
+----+---+----+----+----+----+----+-------------+------------+-------------+
I need to generate Temporary Table? or WITH clause, that will look like:
In case that TransferID = 1 THEN it will take the string from C1 and EndPoint and value from Transferred:
+------+----+------------+
| From | To |Transferred |
+------+----+------------+
| A | B | 80 |
+------+----+------------+
+------+----+------------+
| From | To |Transferred |
+------+----+------------+
| B | C | 25 |
+------+----+------------+
+------+----+------------+
| From | To |Transferred |
+------+----+------------+
| C | A | 70 |
+------+----+------------+
In case than TransferID=2 THEN: It will take the value from C1 and C2 and value from Transferred.
The next row would be then the value from C2 and EndPoint and value from Transferred:
+------+----+------------+
| From | To |Transferred |
+------+----+------------+
| A | B | 40 |
+------+----+------------+
| B | C | 40 |
+------+----+------------+
+------+----+------------+
| From | To |Transferred |
+------+----+------------+
| B | C | 30 |
+------+----+------------+
| C | A | 30 |
+------+----+------------+
In Case that TransferID=3 THEN: It will take the value from C1 and C2 and value from Transferred.
The next row would be then the value from C2 and C3 and value from Transferred.
The next row would be then the value from C3 and EndPoint and value from Transferred
+------+----+------------+
| From | To |Transferred |
+------+----+------------+
| A | B | 10 |
+------+----+------------+
| B | C | 10 |
+------+----+------------+
| C | A | 10 |
+------+----+------------+
And so on up to TransferID=5
And then from Temporary Table or With Selection (not sure what will work better), I will select the SUM of Transferred, GROUP BY From, To.
I am using MS SQL 2008 and SQL Fiddle is here
Fiddle Code :
Create TABLE T (
ID int NOT NULL,
C varchar(5) NOT NULL,
C1 varchar(5),
C2 varchar(5),
C3 varchar(5),
C4 varchar(5),
C5 varchar(5),
EndingPoint varchar(5) NOT NULL,
TransferID int NOT NULL,
Transferred int);
INSERT INTO T VALUES (1,'A','A','','','','','B',1,80);
INSERT INTO T VALUES (2,'A','A','B','','','','C',2,40);
INSERT INTO T VALUES (3,'A','A','B','C','','','A',3,10);
INSERT INTO T VALUES (4,'B','B','','','','','C',1,25);
INSERT INTO T VALUES (5,'B','B','C','','','','A',2,30);
INSERT INTO T VALUES (6,'C','C','','','','','A',1,70);
Many thanks in advance!
You can implement the logic using a bunch of union all statements:
select c1 as frompt, endingpoint as topt, transferred
from t
where transferred = 1
union all
select c1 as frompt, c2 as topt, transferred
from t
where transferred = 2
union all
select c2 as frompt, endingpoint as topt, transferred
from t
where transferred = 2
union all
select c1 as frompt, c2 as topt, transferred
from t
where transferred = 3
union all
select c2 as frompt, c3 as topt, transferred
from t
where transferred = 3
union all
select c3 as frompt, endingpoint as topt, transferred
from t
where transferred = 3;
This can actually be simplified to:
select (case when transferred = 1 then c1
when transferred = 2 then c2
when transferred = 3 then c3
end) as frompt, endingpoint as topt, transferred
from t
union all
select c1, c2, transferred
from t
where transferred >= 2
union all
select c2, c3, transferred
from t
where transferred >= 3;
Note: the resulting format is more normalized and it is a better structure for the data and the version you are storing.
You can unpivot those C1-EndingPoint columns and then use the Lead function and order by the C1 to EndingPoint column names (which happen to be in correct order)...
http://sqlfiddle.com/#!3/61e519/2
select * from (
select
ID,
[From] = pt,
[To] = Lead(pt, 1) over(partition by Id order by col),
[Transferred]
from
T
unpivot(
pt for col in (C1, C2, C3, C4, C5, EndingPoint)
) unp
where
pt <> ''
) t
where
[To] is not null
SQL 2008 version without using Lead function...
http://sqlfiddle.com/#!3/61e519/5
with cte as (
select
ID,
[From] = pt,
rn = row_number() over(partition by Id order by col),
[Transferred]
from
T
unpivot(
pt for col in (C1, C2, C3, C4, C5, EndingPoint)
) unp
where
pt <> ''
)
select
c.[ID],
c.[From],
[To] = n.[From],
c.[Transferred]
from
cte c
inner join cte n on n.ID = c.ID and n.rn = c.rn + 1
Here is the SQL2008 version of the #dotjo's solution:
;with r as (
select *
from T
unpivot(pt for col in (C1, C2, C3, C4, C5, EndingPoint)) unp
where pt!='')
select r.pt FromPt, r2.pt ToPt, sum(r.Transferred) Transferred
from r
cross apply (select top 1 *
from r r2
where r.ID=r2.ID
and r.col<r2.col
order by r2.col) r2
group by r.pt, r2.pt
When I change my #TransferID to each of your specified numbers(1 through 3), I get the same results you do. Hope this helps!
SQL Server 2005 and Above Solution
DECLARE #TransferID INT = 1;
WITH CTE_Unpivot
AS
(
SELECT EndingPoint,TransferID,val,col,ID,Transferred
FROM T
UNPIVOT
(
val FOR col IN (C1,C2,C3,C4,C5)
) unpvt
WHERE col <= 'C' + CAST(#TransferID AS VARCHAR(5))
AND TransferID <= #TransferID
)
SELECT DISTINCT VAL,CA2.EndingPoint,CA.Transferred
FROM CTE_Unpivot A
CROSS APPLY
(
SELECT TOP 1 Transferred
FROM CTE_Unpivot
WHERE val = A.val
AND col = A.col
AND TransferID = #TransferID
ORDER BY ID
) CA
CROSS APPLY
(
SELECT TOP 1 EndingPoint
FROM CTE_Unpivot
WHERE val = A.val
AND col = A.col
ORDER BY ID
) CA2
WHERE val != ''

How to join tables and get the result from the two tables when they appears in the first table and not in the second or vice versa

I have 2 tables that i want want to join in SQL server 2012. The join must be at the column tblA.customer_id to tblB.customer, after joining the 2 table tblA and tblB, i want if the tblA.date_of_revevenue equals tblB.revenue_date, then add the tblA.revenue + tblB.revenue_amount and display them in 1 row, but and if the tblA.date_of_revevenue differs from tblB.revenue_date then display them in 2 different rows in the result table Tbl_Total_Revenue.
I've passed more than 10 hours trying to join using full outer join but it doesn't work.Please i need your help, guys.
This is the sample tables of what i have and what i am looking for is the result table that looks like the one below.
Thank you
tbl_A
*Customer_id | Date_of_revenue | Revenue
C1 | 201201 | 100
C2 | 201203 | 120.20
C4 | 201304 | 150
C5 | 201401 | 70
tbl_B
*Customer | Revenue_date | Revenue_amount
C1 | 201201 | 50
C2 | 201203 | 200
C3 | 201403 | 250
C5 | 201310 | 50
C4 | 201401 | 20
Result table I want
Tbl_Total_Revenue
*Customer_id | Revenue_date | Total_revenue
C1 | 201201 | 150
C2 | 201203 | 320.20
C3 | 201403 | 250
C4 | 201304 | 150
C4 | 201401 | 20
C5 | 201401 | 70
There is an error in sample data (or expected result), there should be two rows for C5:
select isnull(a.Customer_id, b.Customer), isnull(a.Date_of_revenue, b.Revenue_date),
sum(isnull(a.Revenue,0)+isnull(b.Revenue_amount,0)) as Total_revenue
from tbl_A a
full join tbl_B b on a.Customer_id = b.Customer
and a.Date_of_revenue = b.Revenue_date
group by isnull(a.Customer_id, b.Customer), isnull(a.Date_of_revenue, b.Revenue_date)
order by isnull(a.Customer_id, b.Customer), isnull(a.Date_of_revenue, b.Revenue_date)
Try this:
SELECT Customer_id, Date_of_revenue, SUM(Revenue)
FROM
(
(SELECT Customer_id, Date_of_revenue, Revenue FROM tbl_A)
UNION
(SELECT Customer Customer_id, Revenue_date Date_of_revenue, Revenue_amount Revenue FROM tbl_B)
)
GROUP BY Customer_id, Date_of_revenue

Sql Server get first matching value

I have two tables History and Historyvalues:
History
HID(uniqeidentifier) | Version(int)
a1 | 1
a2 | 2
a3 | 3
a4 | 4
Historyvalues
HVID(uniqeidentifier) | HID(uniqeidentifier) | ControlID(uniqeidentifier) | Value(string)
b1 | a1 | c1 | value1
b2 | a2 | c1 | value2
b3 | a2 | c2 | value3
Now I Need a query where I can get a list with the last historyvalue of each control from a specific Version like:
Get the last values from Version 3 -> receiving ->
HVID | ControlID | Value
b2 | c1 | value2
b3 | c2 | value3
I tried something like this:
Select HVID, ControlId, max(Version), Value from
(
Select HVID, ControlId, Version, Value
from History inner JOIN
Historyvalues ON History.HID = Historyvalues.HID
where Version <= 3
) as a
group by ControlId
order by Version desc
but this does not work.
Are there any ideas?
Thank you very much for your help.
Best regards
Latest version from each control with your specific Version (WHERE t1.Version <= 3)
Query:
SQLFIDDLEExample
SELECT HVID, ControlId, Version, Value
FROM
(
SELECT t2.HVID, t2.ControlId, t1.Version, t2.Value,
ROW_NUMBER() OVER(PARTITION BY t2.ControlId ORDER BY t1.Version DESC) as rnk
FROM History t1
JOIN Historyvalues t2
ON t1.HID = t2.HID
WHERE t1.Version <= 3
) AS a
WHERE a.rnk = 1
ORDER BY a.Version desc
Result:
| HVID | CONTROLID | VERSION | VALUE |
|------|-----------|---------|--------|
| b2 | c1 | 2 | value2 |
| b3 | c2 | 2 | value3 |
here is your solution
Select Historyvalues.HVID,Historyvalues.ControlID,Historyvalues.Value
from Historyvalues
inner join History on Historyvalues.hid=History.hid
where Historyvalues.hvid in (
select MAX(Historyvalues.hvid) from Historyvalues
inner join History on Historyvalues.hid=History.hid
group by ControlID)