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.
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
I have a query like below:
DECLARE #t TABLE
(
EmpName VARCHAR(10)
, Qty INT
, Item VARCHAR(12)
)
INSERT INTO #t
VALUES ('Jane',3,'Dog')
, ('Carle',1,'Cat')
, ('Abay',5,'Goat')
, ('Jane',1,'Dog')
, ('Carle',10,'Cat')
, ('Jane',2,'Dog')
, ('Jane',8,'Goat')
, ('Jane',3,'Ram')
, ('Carle',2,'Dog')
--SELECT * FROM #t
SELECT
EmpName, [Dog], [Cat], [Goat], [Ram]
FROM
(SELECT
EmpName, Qty, Item
FROM #t) AS b
PIVOT(SUM(Qty) FOR Item IN ([Dog], [Cat], [Goat], [Ram])) AS p
And the result is as seen in the screenshot below:
I want to calculate the average Qty across Item without ignoring null values in the calculation. For example, in row 1, EmpName Abay should be 5 divided by 4 (number of columns), as seen in this screenshot:
How do I get the average column?
I'm not really familiar with the PIVOT query, so here is an alternative using conditional aggregation:
SELECT
Empname,
Dog = SUM(CASE WHEN Item = 'Dog' THEN Qty ELSE 0 END),
Cat = SUM(CASE WHEN Item = 'Cat' THEN Qty ELSE 0 END),
Goat = SUM(CASE WHEN Item = 'Goat' THEN Qty ELSE 0 END),
Ram = SUM(CASE WHEN Item = 'Ram' THEN Qty ELSE 0 END),
Average = SUM(ISNULL(Qty, 0))/ 4.0
FROM #t
GROUP BY EmpName;
Note that this will only work if you only have 4 Items. Otherwise, you need to resort to dynamic crosstab.
ONLINE DEMO
For dynamic crosstab, I used a temporary table instead of a table variable:
DECLARE #sql NVARCHAR(MAX) = '';
SELECT #sql =
'SELECT
Empname' + CHAR(10);
SELECT #sql = #sql +
' , SUM(CASE WHEN Item = ''' + Item + ''' THEN Qty ELSE 0 END) AS ' + QUOTENAME(Item) + CHAR(10)
FROM (
SELECT DISTINCT Item FROM #t
) t;
SELECT #sql = #sql +
' , SUM(ISNULL(Qty, 0)) / (SELECT COUNT(DISTINCT Item) * 1.0 FROM #t) AS [Average]' + CHAR(10) +
'FROM #t
GROUP BY EmpName;';
ONLINE DEMO
Try a combination of AVG and ISNULL, i.e. AVG(ISNULL(Dog, 0)).
One simple method is:
select empname, goat, cat, dog, ram,
(coalesce(goat, 0) + coalesce(cat, 0) + coalesce(dog, 0) + coalesce( ram, 0)
) / 4.0 as average
from t;
Another simple method uses outer apply:
select t.*, v.average
from t outer apply
(select avg(coalesce(x, 0))
from (values (t.goat), (t.cat), (t.dog), (t.ram)
) v(x)
) v(average);
DECLARE #t TABLE
(
EmpName VARCHAR(10)
, Qty INT
, Item VARCHAR(12)
)
INSERT INTO #t
VALUES ('Jane',3,'Dog')
, ('Carle',1,'Cat')
, ('Abay',5,'Goat')
, ('Jane',1,'Dog')
, ('Carle',10,'Cat')
, ('Jane',2,'Dog')
, ('Jane',8,'Goat')
, ('Jane',3,'Ram')
, ('Carle',2,'Dog')
SELECT EmpName
, [Dog]
, [Cat]
, [Goat]
, [Ram]
,p.total/4.0 as av
FROM (SELECT EmpName, Qty, Item,SUM(qty)OVER(PARTITION BY EmpName) AS total FROM #t) AS b
PIVOT(SUM(Qty) FOR Item IN([Dog],[Cat],[Goat],[Ram])) AS p
EmpName Dog Cat Goat Ram av
---------- ----------- ----------- ----------- ----------- ---------------------------------------
Abay NULL NULL 5 NULL 1.250000
Carle 2 11 NULL NULL 3.250000
Jane 6 NULL 8 3 4.250000
V2: Dynamic script:
CREATE TABLE #t
(
EmpName VARCHAR(10)
, Qty INT
, Item VARCHAR(12)
)
INSERT INTO #t
VALUES ('Jane',3,'Dog')
, ('Carle',1,'Cat')
, ('Abay',5,'Goat')
, ('Jane',1,'Dog')
, ('Carle',10,'Cat')
, ('Jane',2,'Dog')
, ('Jane',8,'Goat')
, ('Jane',3,'Ram')
, ('Carle',2,'Dog')
INSERT #t ( EmpName, Qty, Item )VALUES('Abay',100,'abc')
DECLARE #cols VARCHAR(max),#sql VARCHAR(MAX),#cnt INT
SELECT #cols=ISNULL(#cols+',[','[')+Item+']',#cnt=ISNULL(#cnt+1,1) FROM #t GROUP BY Item
PRINT #cols
PRINT #cnt
SET #sql='SELECT EmpName, '+#cols+',p.total*1.0/'+LTRIM(#cnt)+' as av'+CHAR(13)
+' FROM (SELECT EmpName, Qty, Item,SUM(qty)OVER(PARTITION BY EmpName) AS total FROM #t) AS b'+CHAR(13)
+' PIVOT(SUM(Qty) FOR Item IN('+#cols+')) AS p'
EXEC(#sql)
EmpName abc Cat Dog Goat Ram av
---------- ----------- ----------- ----------- ----------- ----------- ---------------------------------------
Carle NULL 11 2 NULL NULL 2.600000
Jane NULL NULL 6 8 3 3.400000
Abay 100 NULL NULL 5 NULL 21.000000
Avoid NULL from your pivot sentence and compute AVG.
;with ct as
(
SELECT EmpName
, ISnull([Dog],0) Dog
, ISnull([Cat],0) Cat
, ISnull([Goat],0) Goat
, ISnull([Ram],0) Ram
FROM (SELECT EmpName, Qty, Item FROM #t) AS b
PIVOT(SUM(Qty) FOR Item IN([Dog],[Cat],[Goat],[Ram])) AS p
)
select empname, avg(dog) dog, avg(cat) cat, avg(goat) goat, avg(ram) ram
from ct
group by empname;
+---------+-----+-----+------+-----+
| empname | dog | cat | goat | ram |
+---------+-----+-----+------+-----+
| Abay | 0 | 0 | 5 | 0 |
+---------+-----+-----+------+-----+
| Carle | 2 | 11 | 0 | 0 |
+---------+-----+-----+------+-----+
| Jane | 6 | 0 | 8 | 3 |
+---------+-----+-----+------+-----+
SELECT EmpName
, [Dog]
, [Cat]
, [Goat]
, [Ram]
,(isnull(p.cat,0)+isnull(p.dog,0)+isnull(p.Goat,0)+isnull(p.Ram,0))/4.0 as average
FROM (SELECT EmpName, Qty, Item FROM #t) AS b
PIVOT(SUM(Qty) FOR Item IN([Dog],[Cat],[Goat],[Ram])) AS p
This question already has answers here:
Advice Using Pivot Table in Oracle
(4 answers)
Closed 8 years ago.
How to display the number of employees in each department with each salary in oracle
something like this
department_id salar_equal_1000 salar_equal_20000 city_boston city_detroit city_none
10 2 3 2 1 2
20 1 2 1 2 0
table contains data like this
department_id salary city
10 1000 boston
10 1000 boston
10 2000 detroit
10 2000
10 2000
20 1000 boston
20 2000 detroit
20 2000 detroit
I guess i have to use the pivot statement but i am not sure need some help on this
Try this:
select
department_id,
sum(case when salary = 1000 then 1 else 0 end) as salar_equal_1000,
sum(case when salary = 2000 then 1 else 0 end) as salar_equal_2000,
sum(case when city = 'boston' then 1 else 0 end) as city_boston,
sum(case when city = 'detroit' then 1 else 0 end) as city_detroit,
sum(case when city is null then 1 else 0 end) as city_none
from
employees
group by department_id
order by department_id
Edited again:
See it here on fiddle: http://sqlfiddle.com/#!4/3564c/6
Hi please go through the following example, Might be it will help you.
If you are working with oracle10g this is the format,
if 11g pivot function is there
WITH cte(dept_id,salary) AS (SELECT 10,1000 FROM dual
UNION ALL
SELECT 10,1000 FROM dual
UNION ALL
SELECT 10,1000 FROM dual
UNION ALL
SELECT 20,1000 FROM dual
UNION ALL
SELECT 10,2000 FROM dual
UNION ALL
SELECT 10,2000 FROM dual
UNION ALL
SELECT 20,1000 FROM dual
UNION ALL
SELECT 20,2000 FROM dual
UNION ALL
SELECT 20,2000 FROM dual
),
TEMP as(SELECT
dept_id,
count(salary) cnt,salary
FROM
cte
GROUP BY salary, dept_id)
SELECT dept_id,sum(CASE WHEN salary=1000 THEN cnt ELSE 0 END)salary_1000,
sum(CASE WHEN salary=2000 THEN cnt ELSE 0 END) salary_2000 FROM TEMP
GROUP BY dept_id
I have been asked to display X number of rows, into a row that is going across with results. So for example :-
The Data
Tency Number| ClientNo | Name | DOB | Prim Client | Rent
10001 20 Joe 01/10/1900 Y 100
10001 21 bob 01/10/1901 N 100
10001 26 jim 01/10/1902 n 100
The format the user would like is
Tency Number | ClientNo | Name | DOB | Prim Client | Rent | Client2 | DOB 2|
10001 20 Joe 01/10/1900 Y 100 | Bob 01/10/1901 | Jim | 01/10/1902
We haven't got a pre defined number of client linked to Tenacy Number. I'm lost at what to use to achieve this. There is nothing there to pivot the table, and I used STUFF to link all the client into one column on the row (But they didn't want that).
Is this possible? And if so how would I achieve it?
Thank you!
The SQL you would need would be something like:
SELECT TencyNumber,
ClientNo = MIN(CASE WHEN RowNum = 1 THEN ClientNo END),
Client1 = MAX(CASE WHEN RowNum = 1 THEN Name END),
DOB1 = MAX(CASE WHEN RowNum = 1 THEN DOB END),
Client2 = MAX(CASE WHEN RowNum = 2 THEN Name END),
DOB2 = MAX(CASE WHEN RowNum = 2 THEN DOB END),
Client3 = MAX(CASE WHEN RowNum = 3 THEN Name END),
DOB3 = MAX(CASE WHEN RowNum = 3 THEN DOB END)
FROM ( SELECT TencyNumber,
ClientNo,
Name,
DOB,
PrimClient,
Rent,
RowNum = ROW_NUMBER() OVER(PARTITION BY TencyNumber ORDER BY Name)
FROM T
) c
GROUP BY TencyNumber;
But since you have an unknown number of clients, you would need to build the SQL dynamically:
DECLARE #SQL NVARCHAR(MAX) = '';
SELECT #SQL = #SQL +
',Client' + RowNum + ' = MAX(CASE WHEN RowNum = ' + RowNum + ' THEN Name END)
,DOB' + RowNum + ' = MAX(CASE WHEN RowNum = ' + RowNum + ' THEN DOB END)'
FROM ( SELECT RowNum = CAST(ROW_NUMBER() OVER(PARTITION BY TencyNumber ORDER BY Name) AS VARCHAR(10))
FROM T
) c;
SET #SQL = 'SELECT TencyNumber, ClientNo = MIN(CASE WHEN RowNum = 1 THEN ClientNo END) ' + #SQL + '
FROM ( SELECT TencyNumber,
ClientNo,
Name,
DOB,
PrimClient,
Rent,
RowNum = ROW_NUMBER() OVER(PARTITION BY TencyNumber ORDER BY Name)
FROM T
) c
GROUP BY TencyNumber;';
EXECUTE SP_EXECUTESQL #SQL;
Example on SQL Fiddle