count on one column and group by another column - sql

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

Related

Unpivot (or alternative) all columns into rows

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;

SQL combine rows based on group by in view

I have a table:
c1 c2
----------
A B
A C
X P
X Q
How can i create a view that shows this as
c1 c2 c3
---------------
A B C
X P Q
You can use aggregation:
select c1, min(c2) as c2, max(c2) as c3
from t
group by c1;
You can self join the table:
select
t1.c1, t1.c2, t2.c2 as c3
from tablename t1 inner join tablename t2
on t1.c1 = t2.c1 and t1.c2 < t2.c2
See the demo.
Results:
| c1 | c2 | c3 |
| --- | --- | --- |
| A | B | C |
| X | P | Q |

Select Distinct pair of column values

I have a table like this
A1 | A2
a | b
c | d
b | a
a | b
And I want to select distinct pairs :
A1 | A2
a | b
c | d
I tried :
select a, b from (
select a, b , a|b as ab, b|a as ba from T
)t where ab!=ba group by a, b
Anyone have a better idea about how I can do this ?
Thanks
An ANSI compliant way of doing this would be to rearrange each pair of A1 and A2 values as min/max using CASE expressions. Then just select distinct on this derived table.
SELECT DISTINCT
A1, A2
FROM
(
SELECT
CASE WHEN A1 < A2 THEN A1 ELSE A2 END AS A1,
CASE WHEN A1 < A2 THEN A2 ELSE A1 END AS A2
FROM yourTable
) t
This would be the cleanest way if NULL values are not involved
select distinct
least (A1,A2) as A1
,greatest (A1,A2) as A2
from t
;
+-----+-----+
| a1 | a2 |
+-----+-----+
| a | b |
| c | d |
+-----+-----+

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 != ''

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)