SQL count how many times a value appears across multiple columns? - sql

Below is an example of a table in our CRM, its not the way I'd have chosen to store this data but thats by the by, What would be the 'nice' way to count how many times each option was selected by each team?
asking here before i go headlong into a convoluted case statement :)
+----------+--------+---------+---------+---------+
| PersonID | Team | Option1 | Option2 | Option3 |
+----------+--------+---------+---------+---------+
| 1 | Blue | A | B | C |
| 2 | Blue | B | C | D |
| 3 | Blue | D | A | E |
| 4 | Red | A | B | D |
| 5 | Red | B | A | C |
| 6 | Yellow | A | B | C |
| 7 | Yellow | A | C | D |
+----------+--------+---------+---------+---------+
Thanks in advance

You can unpivot your 3 option columns into a single column using CROSS APPLY and a table value constructor and then perform your count:
SELECT t.Team, upvt.[Option], COUNT(*) AS Occurances
FROM dbo.T
CROSS APPLY (VALUES (t.Option1), (t.Option2), (t.Option3)) AS upvt ([Option])
GROUP BY t.Team, upvt.[Option]
ORDER BY t.Team, upvt.[Option];
So this would give:
Team Option Occurances
-------------------------------
Blue A 2
Blue B 2
Blue C 2
Blue D 2
Blue E 1
Red A 2
Red B 2
Red C 1
Red D 1
Yellow A 2
Yellow B 1
Yellow C 2
Yellow D 1

You can use UNION ALL to move the values into single column and then, do aggregation:
select
team, option, count(*) cnt
from(
select team, option1 option from t union all
select team, option2 from t union all
select team, option3 from t
)t group by team, option;

Related

Count the number of appearances of char given a ID

I have to perform a query where I can count the number of distinct codes per Id.
|Id | Code
------------
| 1 | C
| 1 | I
| 2 | I
| 2 | C
| 2 | D
| 2 | D
| 3 | C
| 3 | I
| 3 | D
| 4 | I
| 4 | C
| 4 | C
The output should be something like:
|Id | Count | #Code C | #Code I | #Code D
-------------------------------------------
| 1 | 2 | 1 | 1 | 0
| 2 | 3 | 1 | 0 | 2
| 3 | 3 | 1 | 1 | 1
| 4 | 2 | 2 | 1 | 0
Can you give me some advise on this?
This answers the original version of the question.
You are looking for count(distinct):
select id, count(distinct code)
from t
group by id;
If the codes are only to the provided ones, the following query can provide the desired result.
select
pvt.Id,
codes.total As [Count],
COALESCE(C, 0) AS [#Code C],
COALESCE(I, 0) AS [#Code I],
COALESCE(D, 0) AS [#Code D]
from
( select Id, Code, Count(code) cnt
from t
Group by Id, Code) s
PIVOT(MAX(cnt) FOR Code IN ([C], [I], [D])) pvt
join (select Id, count(distinct Code) total from t group by Id) codes on pvt.Id = codes.Id ;
Note: as I can see from sample input data, code 'I' is found in all of Ids. Its count is zero for Id = 3 in the expected output (in the question).
Here is the correct output:
DB Fiddle

postgresql : join with condition and without duplication

I have a table A that contains a series of unique id. I have an other table B that contains some of this id but not each one of them, a field called value and a another field called idcategory. In this table B, id can appears several times because of differents categories.
I want to list all my id in the table A in a unique way and the specific value associated in the table B in a defined categorie (idcategorie = 1). Id in the table A could not appear in the table B, but i want this information anyway in my final result and without duplication.
Here is an illustration :
Table A
id
-----
1
2
3
4
5
6
7
8
Table B
id | idcategory | value
------------------------
1 | 1 | red
1 | 2 | circle
2 | 1 | green
3 | 1 | blue
3 | 2 | square
4 | 1 | green
4 | 2 | circle
5 | 1 | red
5 | 2 | square
8 | 2 | circle
Result
id | idcategory | value
------------------------
1 | 1 | red
2 | 1 | green
3 | 1 | blue
4 | 1 | green
5 | 1 | red
6 | null | no value
7 | null | no value
8 | null | no value
What is the best way to achieve this in postgreSQL ? LEFT JOIN ? UNION ?
You seem to want a left join:
select a.id, b.idcategory, b.value
from a left join
b
on b.id = a.id and b.idcategory = 1;
The value column has NULL rather than 'no value'. You can replace it, but NULL usually serves that purpose.

SQL Server - List single row multiple times in query result

I have a dataset in a SQL Server database, and I'd like to list run a query to list records from a table, multiple times according to the respective integer value in a column of that table.
I've seen many of the posts about listing a record multiple times in a query, with most of the more suitable responses describing a solution involving Cross join. The situation I'm describing is slightly different from what I've seen of yet, however.
The table structure of the records is akin to the following:
+-----+-------+------+------+
| id | name | type | num |
+-----+-------+------+------+
| 1 | bob | red | 1 |
+-----+-------+------+------+
| 2 | sam | blue | 3 |
+-----+-------+------+------+
| 3 | viv | green| 2 |
+-----+-------+------+------+
I'd like to display this in a query result as follows...
+-----+-------+------+
| id | name | type |
+-----+-------+------+
| 1 | bob | red |
+-----+-------+------+
| 2 | sam | blue |
+-----+-------+------+
| 2 | sam | blue |
+-----+-------+------+
| 2 | sam | blue |
+-----+-------+------+
| 3 | viv | green|
+-----+-------+------+
| 3 | viv | green|
+-----+-------+------+
... where each record appears multiple times according to the number it is listed in the original row. (bob * 1, sam * 3, viv * 2)
Is this possible via cross-join or any other method available?
Note: This does not need to be terribly efficient.
You can try to use recursive CTE
;with cte as (
SELECT id,name,type,1 startnum,num
FROM T
UNION ALL
SELECT id,name,type , startnum+1,num
FROM cte
WHERE startnum+1<=num
)
SELECT id,name,type
FROM cte
order by id
sqlfiddle
Another option is an ad-hoc tally/numbers table
Example
Select A.ID
,A.[Name]
,A.[Type]
From YourTable A
Cross Apply (Select Top (A.[num]) N=Row_Number() Over (Order By (Select NULL)) From master..spt_values n1) B
Or a simple JOIN
Select A.ID
,A.[Name]
,A.[Type]
From YourTable A
Join (Select Top (100) N=Row_Number() Over (Order By (Select NULL)) From master..spt_values n1) B
on B.N<=A.[Num]
Both would Return
ID Name Type
1 bob red
2 sam blue
2 sam blue
2 sam blue
3 viv green
3 viv green
Use a Stored procedure
create a temporary table variable
Write insert into temporary table in a while loop
while counter < max(num)
insert into table where counter <=num
Finally select from table .
EDITED:
(Thanks, #MatBailie!)
Using a recursive CTE on the num column to generate the required number of repetitions, and doing a left-join:
Given table so1 as follows:
id name type num
1 bob red 1
2 sam blue 3
3 viv green 2
4 jon grey 7
Try:
WITH RECURSIVE exp_num AS
(
SELECT id, num FROM so1
UNION
SELECT id, num - 1 FROM exp_num WHERE num > 1
)
SELECT s1.* FROM so1 s1
LEFT JOIN
(
SELECT id FROM exp_num
) s2
ON s1.id = s2.id
Output:
id name type
1 bob red
2 sam blue
2 sam blue
2 sam blue
3 viv green
3 viv green
4 jon grey
4 jon grey
4 jon grey
4 jon grey
4 jon grey
4 jon grey
4 jon grey

SSRS Multiple Lines of data

My SQL Server 2008 report returns me multiple lines in a table. A simple select statement returns two columns, one is the record number the other is a sector. The sector column can contain any one of 6 different values.
EDIT: NULL values are allowed in the sector column.
I want this data to be in one line in my table.
Lets say record number 1 has Sector A, Sector C and Sector E
and Record 2 has Sector B and Sector C
And Record 3 has none.
I am after three lines of data. I also need it displayed two ways. One so that all sectors appear in the same cell separated by commas. The other is a separate Cell for each category
Record number | Sector
1 | A, C, E
2 | B, C
3 |
Or
Record Number | Sector A | Sector B | Sector C | Sector D | Sector E
1 | A | | C | | E
2 | | B | C | |
3 | | | | |
At the moment my report gives me 6 rows.
Record Number | Sector
1 | A
1 | C
1 | E
2 | B
2 | C
3 |
Is there a way of working around this problem?
I am using the query designer rather than writing the SQL statements.
In order to get the first result that you want with the comma-separated list of sector values, you will want to use FOR XML PATH and STUFF. The code will be:
select t1.recordnumber,
STUFF((SELECT ', ' + t2.sector
from yourtable t2
where t1.recordnumber = t2.recordnumber
FOR XML PATH (''))
, 1, 1, '') AS Sector
from yourtable t1
group by t1.recordnumber
See SQL Fiddle with Demo. The result is:
| RECORDNUMBER | SECTOR |
---------------------------
| 1 | A, C, E |
| 2 | B, C |
| 3 | (null) |
Then to get the result of the recordNumber in a single row, you can use the PIVOT function:
select *
from
(
select recordNumber, sector
from yourtable
) src
pivot
(
max(sector)
for sector in (A, B, C, D, E)
) piv;
See SQL Fiddle with Demo. The result of this query is:
| RECORDNUMBER | A | B | C | D | E |
-------------------------------------------------------------
| 1 | A | (null) | C | (null) | E |
| 2 | (null) | B | C | (null) | (null) |
| 3 | (null) | (null) | (null) | (null) | (null) |

Display Rows side by side, Kind of pivot

I have a table like this
Column1 | Column2
-------------------
A | 1
A | 2
A | 3
B | 4
B | 5
B | 3
C | 2
C | 2
C | 2
D | 7
D | 8
D | 9
I want to output it as
A | B | C | D
--------------------
1 | 4 | 2 | 7
2 | 5 | 2 | 8
3 | 3 | 2 | 9
It will have fixed Rows/Columns like A,B,C,D.
Could you suggest a query in SQL Server 2005/2008?
it's better to know your clustered key in the table, since the order might be different after the result. Martin is right, try this out, it will get you started:
SELECT pvt.A,
pvt.B,
pvt.C,
pvt.D
FROM (SELECT *,
row=ROW_NUMBER() OVER(PARTITION BY Column1 ORDER BY (SELECT 1))
FROM yourtable) AS A
PIVOT (MIN(Column2) FOR Column1 IN ([A], [B], [C], [D]))
AS pvt