Count grouped values - sql

Again I need some help.
I have a table (for the sake of simplicity) with 3 fields.
code id letter
1 2016 Pablo A
2 2017 Pablo B
3 2016 Ana B
4 2017 Pablo A
5 2018 Ana A
6 2018 Ana A
I need a query that results in
code id letterA letterB
1 2016 Pablo 1 Null
2 2017 Pablo 1 1
3 2016 Ana Null 1
4 2018 Ana 2 Null
As you can see I count the records for id and grouped by code, if they have different letters for code a new record appears, but if they have both letters on the same code is just one record.
I tried with UNION but what I got is two records (with the same code) with different letters.
Thanks guys,
Edit one:
The query with union
select code, id, count(id), 'letter A' letter
from table
where letter = 'A'
union
select code, id, count(id), 'letter B' letter
from table
where letter = 'B'
I got something like this
code id count(id) letter
1 2016 Pablo 1 A
2 2017 Pablo 1 A
3 2017 Pablo 1 B
4 2016 Ana 1 B
5 2018 Ana 2 A
The problem is that I have 2 code 2017 with id Pablo, I would like to have just 1

You almost got it. You only need another GROUP BY to get the result that you wanted.
Using PIVOT
select *
from tbl t
pivot
(
count(letter)
for letter in ([A], [B])
) p
order by id desc, code
Using Union All
select code, id, A = sum(A), B = sum(B)
from
(
select code, id, A = count(*), B = null
from tbl t
where letter = 'A'
group by code, id
union all
select code, id, A = null, B = count(*)
from tbl t
where letter = 'B'
group by code, id
) d
group by code, id
order by id desc, code

You can do this by executing a dynamic sql query rather than giving values explicitly.
Query
declare #sql as varchar(max);
select #sql = 'select [code], [id], ' + stuff((
select distinct ', sum(case [letter] when ' + char(39) + [letter] + char(39)
+ ' then 1 else 0 end) as [letter' + [letter] + '] '
from [dbo].[your_table_name]
for xml path('')
)
, 1, 2, ''
);
select #sql += ' from [dbo].[your_table_name] group by [code], [id] order by [id];';
exec(#sql);

Other approach is using CASE expression in SELECT by grouping rows.
select code,
id,
SUM(CASE WHEN letter= 'A' THEN 1 ELSE 0 END) AS 'letter A' ,
SUM(CASE WHEN letter= 'B' THEN 1 ELSE 0 END) AS 'letter B'
from table
group by code, id
Note: If there are no letters, then it returns 0 instead of NULL.

Related

How to combine each three rows into single column in SQL Server 2008?

NOOfDays DISTRInutorID
-------------------------
1 abcd
1 cdef
2 DFSDF
2 SFSDD
2 SDFSD
2 WAOYWAR
7 WEFIWE
7 WEOFYWE
7 WFYREU
The above is one of my sample tables, I want to combine each two rows based on NOOfDays.
Expected output:
NOOfDays DiSTRInutorID
------------------------------
1 abcd, cdef
2 DFSDF, SFSDD
2 SDFSD, WAOYWAR
7 WEFIWE, WEOFYWE
7 WFYREU
In ancient versions of SQL Server, the logic looks like:
select n.NOOfDays,
stuff( (select ',' + t2.DISTRInutorID
from t t2
where t2.NOOfDays = n.NOOfDays
for xml path ('')
), 1, 1, ''
) as list
from (select distinct NOOfDays from t) n;
EDIT:
I misunderstood the original question. This is what you are looking for:
with cte as (
select t.*, row_number() over (order by (select null)) - 1 as seqnum
from t
)
select NOOfDays,
(case when count(*) = 1 then min(DISTRInutorID)
else max(case when seqnum % 2 = 0 then DISTRInutorID end) + ',' + max(case when seqnum % 2 = 1 then DISTRInutorID end)
end) as list
from cte
group by NOOfDays, floor(seqnum / 2);
Note that SQL tables represent unordered sets. This arbitrarily pairs rows where the first column is the same, but there is no guarantee that these are adjacent -- for the simple reason that "adjacent" is not defined.
Here is a db<>fiddle.

Display Column Values for last record only in sql server

Hi Iam having table like below,
Name RN AGE
A 1 21
B 2 22
C 3 23
I want to display age for last record only remaining column value as empty like below,
Name RN AGE
A 1
B 2
C 3 23
Use window function
SELECT *,
(CASE WHEN MAX(AGE) OVER () = AGE THEN CAST(AGE AS VARCHAR(10)) ELSE '' END) AGE1
FROM table
You could also use CROSS JOIN
SELECT *,
(CASE WHEN t.AGE <> c.MAXAGE THEN '' ELSE CAST(t.AGE AS VARCHAR(10)) END) AGE1
FROM table t CROSS JOIN (
SELECT MAX(AGE) AS MAXAGE FROM table) c
This assumes last records in terms of AGE else you would need to RN/Name column to be aggregate and use them.

SQL Server (2012): Pivot (or Group by?) on Multiple Columns

I have the following table:
month seating_id dept_id
Jan 1 5
Jan 8 9
Jan 5 3
Jan 7 2
Jan 1 5
Feb 1 9
Feb 8 9
Feb 5 3
Feb 7 2
Feb 7 1
I want to count each type of seating_id and dept_id for each month, so the result would look something like this:
month seating_id_1 seating_id_5 seating_id_7 seating_id_8 dept_id_1 dept_id_2 dept_id_3 dept_id_5 dept_id_9
Jan 2 1 1 1 0 1 1 2 1
Feb 0 1 2 1 1 1 1 0 2
I've experimented with unpivot/pivot and GROUP BY but haven't been able to achieve the desired results. Note, I would like to perform this as a SELECT statement, and not PROCEDURE call, if possible.
Let me know if you need any other info.
In case you need to go Dynamic
Example
Declare #SQL varchar(max) = '
Select *
From (
Select month,B.*
From YourTable A
Cross Apply (values (concat(''seating_id_'',seating_id),seating_id)
,(concat(''dept_id_'',dept_id),dept_id)
) b(item,value)
) A
Pivot (count([Value]) For [Item] in (' + Stuff((Select Distinct concat(',[seating_id_',seating_id,']') from YourTable For XML Path('')),1,1,'')
+','+
Stuff((Select Distinct concat(',[dept_id_',dept_id,']') from YourTable For XML Path('')),1,1,'')
+ ') ) p'
Exec(#SQL);
Returns
The Generated SQL Looks like this
Select *
From (
Select month,B.*
From YourTable A
Cross Apply (values (concat('seating_id_',seating_id),seating_id)
,(concat('dept_id_',dept_id),dept_id)
) b(item,value)
) A
Pivot (count([Value]) For [Item] in ([seating_id_1],[seating_id_5],[seating_id_7],[seating_id_8],[dept_id_1],[dept_id_2],[dept_id_3],[dept_id_5],[dept_id_9]) ) p
i have an answer you might not like but it does it:
select month
, sum(case seating_id when 1 then 1 else 0 end) as seating_id_1
, sum(case seating_id when 2 then 1 else 0 end) as seating_id_2
...
, sum(case dept_id when 1 then 1 else 0 end) as dept_id_1
, sum(case dept_id when 2 then 1 else 0 end) as dept_id_2
...
from YourTable
group by month

Multiple rows show in a single row using query in sql?

I want to display the department no. and the number of employees in each department from EMP table in a single row. I had one query which display the result in separate rows.
select deptno, count(*) from emp
group by deptno;
Dptno Count(*)
10 5
20 3
30 4
I want to display the result as a single-row. For example:
Dpt10 Count(*) Dpt20 Count(*) Dpt30 Count(*)
10 5 20 3 30 4
The output in this forum is not proper but try to understand that the no. 5,3 & 4 should be below count(*) column and 10,20 & 30 should be below deptno.
Since pivot doesn't support several aggregates in SQL Server:
with t as (
select 10 id, 15 su union all
select 10 id, 10 su union all
select 10 id, 5 su union all
select 20 id, 135 su union all
select 20 id, 100 su union all
select 20 id, 15 su union all
select 30 id, 150 su union all
select 30 id, 1000 su union all
select 30 id, 500 su
)
select max(case when id = 10 then id end) dept10
, count(case when id = 10 then id end) dept10_cnt
, max(case when id = 20 then id end) dept20
, count(case when id = 20 then id end) dept20_cnt
, max(case when id = 30 then id end) dept30
, count(case when id = 30 then id end) dept30_cnt
from t
SQLFiddle
In SQL Server, there are several ways that you can convert multiple rows of data into columns.
If you needed to just convert the count of each deptno into columns, then you could do this easily with either the PIVOT function or a CASE/aggregate combination
select
sum(case when deptno = 10 then 1 else 0 end) Count_Dpt10,
sum(case when deptno = 20 then 1 else 0 end) Count_Dpt20,
sum(case when deptno = 30 then 1 else 0 end) DCount_pt30
from emp
See Demo
But part of the problem is that you want to pivot both the DeptNo and the Total_Count into column - this would need to use 2 different aggregate functions. In your situation the PIVOT function won't work, so you'd have to use a different aggregate function along with a CASE expression similar to:
select
max(case when deptno = 10 then deptno end) Dpt10,
sum(case when deptno = 10 then 1 else 0 end) Count_Dpt10,
max(case when deptno = 20 then deptno end) Dpt20,
sum(case when deptno = 20 then 1 else 0 end) Count_Dpt20,
max(case when deptno = 30 then deptno end) Dpt30,
sum(case when deptno = 30 then 1 else 0 end) DCount_pt30
from emp;
See SQL Fiddle with Demo. Now since you have unknown departments, so you'll need use dynamic sql. This will create a sql string that will then be executed. To create the sql string you'll need to use STUFF and FOR XML PATH. The dynamic code would be:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols
= STUFF((SELECT
', max(case when deptno = '+cast(deptno as varchar(10))+' then deptno end) as '+ QUOTENAME('Dpt'+cast(deptno as varchar(10)))
+ ', sum(case when deptno = '+cast(deptno as varchar(10))+' then 1 else 0 end) as '+ QUOTENAME('Count_Dpt'+cast(deptno as varchar(10)))
from emp
group by deptno
order by deptno
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'SELECT ' + #cols + '
from emp'
exec sp_executesql #query;
See SQL Fiddle with Demo. this gives you a result:
| DPT10 | COUNT_DPT10 | DPT20 | COUNT_DPT20 | DPT30 | COUNT_DPT30 |
|-------|-------------|-------|-------------|-------|-------------|
| 10 | 5 | 20 | 3 | 30 | 4 |
You can try this -
Schema
DECLARE #emp TABLE ([deptno] int NULL, [deptcount] int NULL);
INSERT #emp ([deptno], [deptcount]) VALUES (10, 5), (20, 3), (30, 4);
Query
SELECT STUFF((
SELECT ' ' + CAST([deptno] AS VARCHAR) + ' ' + CAST([deptcount] AS VARCHAR)
FROM #emp
FOR XML PATH('')
), 1, 1, '')
OutPut
10 5 20 3 30 4

SQL union same number of columns, same data types, different data

I have two result sets that look approximately like this:
Id Name Count
1 Asd 1
2 Sdf 4
3 Dfg 567
4 Fgh 23
But the Count column data is different for the second one and I would like both to be displayed, about like this:
Id Name Count from set 1 Count from set two
1 Asd 1 15
2 Sdf 4 840
3 Dfg 567 81
4 Fgh 23 9
How can I do this in SQL (with union if possible)?
My current SQL, hope this will better explain what I want to do:
(SELECT Id, Name, COUNT(*) FROM Customers where X)
union
(SELECT Id, Name, COUNT(*) FROM Customers where Y)
select *
from
(
SELECT 'S1' as dataset, Id, Name, COUNT(*) as resultcount FROM Customers where X
union
SELECT 'S2',Id, Name, COUNT(*) FROM Customers where Y
) s
pivot
(sum(resultcount) for dataset in (s1,s2)) p
You can do something like:
;WITH Unioned
AS
(
SELECT 'Set1' FromWhat, Id, Name FROM Table1
UNION ALL
SELECT 'Set2', Id, Name FROM Table2
)
SELECT
Id,
Name,
SUM(CASE FromWhat WHEN 'Set1' THEN 1 ELSE 0 END) 'Count from set 1',
SUM(CASE FromWhat WHEN 'Set2' THEN 1 ELSE 0 END) 'Count from set 2'
FROM Unioned
GROUP BY Id, Name;
SQL Fiddle Demo