T-SQL: Count and Group By Across Multiple Columns - sql

I have the following table:
Name | Col1 | Col2 | Col3
John A B C
John A D A
Bill A A D
Bill F A A
Steve F A B
Steve C C A
I want to know a total of how many As John, Bill, and Steve have...
e.g. John: 3, Bill: 4, Steve: 2
How could I do this using T-SQL? Thanks.

You can use the UNPIVOT function:
select name, count(val) TotalAs
from yourtable
unpivot
(
val
for col in (col1, col2, col3)
) u
where val = 'A'
group by name
See SQL Fiddle with Demo

Try this SQL Fiddle For below:
declare #table table (name varchar(10), c1 char(1), c2 char(1), c3 char(1))
insert into #table
SELECT 'John','A','B','C' UNION
SELECT 'John','A','D','A' UNION
SELECT 'Bill','A','A','D' UNION
SELECT 'Bill','F','A','A' UNION
SELECT 'Steve','F','A','B' UNION
SELECT 'Steve','C','C','A'
; WITH CTE AS
(
SELECT name,
(SELECT Count(CASE WHEN val = 'A' Then 1 ELse Null end)
FROM (VALUES (c1),(c2),(c3)) AS value(val)) As ACOL
from #table
)
SELECT Name, SUM(ACOL) ACount FROM CTE GROUP BY NAME

How about this for SQL Server 2012? It may be faster.
select name,sum(datalength(concat(col1,col2,col3))-
datalength(replace(concat(col1,col2,col3),'A','')))
from yourtable
group by name;

Related

Case when duplicate add one more letter

For example: I have a table with these records below
1 A
2 A
3 B
4 C
...
and I need to migrate these record in to another table
1 AA
2 AB
3 B
4 C
...
Meaning if the record is duplicate, it will automatically add one more letter alphabetically.
Just a slightly different approach
Example
Declare #YourTable Table (ID int,[SomeCol] varchar(50))
Insert Into #YourTable Values
(1,'A')
,(2,'A')
,(3,'B')
,(4,'C')
Select *
,NewVal = concat(SomeCol,IIF(sum(1) over (partition by SomeCol)=1,'',char(64+row_number() over ( partition by SomeCol order by ID ))) )
From #YourTable
Returns
ID SomeCol NewVal
1 A AA
2 A AB
3 B B
4 C C
EDIT - Requested UPDATE
Declare #YourTable Table (ID int,[SomeCol] varchar(50))
Insert Into #YourTable Values
(1,'A')
,(2,'A')
,(3,'B')
,(4,'C')
Select *
,NewVal = concat(SomeCol,IIF(sum(1) over (partition by SomeCol)=1,'',replace(char(63+row_number() over ( partition by SomeCol order by ID )),'#','')) )
From #YourTable
Returns
ID SomeCol NewVal
1 A A
2 A AA
3 B B
4 C C
We might be able to handle this requirement with the help of a calendar table mapping secondary letters to duplicate sequence counts:
WITH letters AS (
SELECT 1 AS seq, 'A' AS let UNION ALL
SELECT 2, 'B' UNION ALL
SELECT 3, 'C' UNION ALL
...
SELECT 26, 'Z' UNION ALL
...
),
cte AS (
SELECT id, let, ROW_NUMBER() OVER (PARTITION BY let ORDER BY id) rn,
COUNT(*) OVER (PARTITION BY let) cnt
FROM yourTable
)
SELECT t1.id, t1.let + CASE WHEN t1.cnt > 1 THEN t2.let ELSE '' END AS let
FROM cte t1
LEFT JOIN letters t2
ON t1.id = t2.seq
ORDER BY t1.id;
Demo

Pivoting row's to columns

How to achieve the below??
Anyone help me out
col_1 col_2
A 1
B 1
C 1
B 2
C 4
A 2
A 6
Output:
A B C
1 1 1
2 2 4
6
This will do the job, but it seems like quite an odd thing to want to do, so I am probably missing something?
CREATE TABLE #table (col1 CHAR(1), col2 INT);
INSERT INTO #table SELECT 'A', 1;
INSERT INTO #table SELECT 'B', 1;
INSERT INTO #table SELECT 'C', 1;
INSERT INTO #table SELECT 'B', 2;
INSERT INTO #table SELECT 'C', 4;
INSERT INTO #table SELECT 'A', 2;
INSERT INTO #table SELECT 'A', 6;
WITH Ranked AS (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY col1 ORDER BY col2) AS rank_id
FROM
#table),
Numbers AS (
SELECT 1 AS number
UNION ALL
SELECT number + 1 FROM Numbers WHERE number < 50)
SELECT
MAX(CASE WHEN col1 = 'A' THEN col2 END) AS [A],
MAX(CASE WHEN col1 = 'B' THEN col2 END) AS [B],
MAX(CASE WHEN col1 = 'C' THEN col2 END) AS [C]
FROM
Numbers n
INNER JOIN Ranked r ON r.rank_id = n.number
GROUP BY
n.number;
Results are:
A B C
1 1 1
2 2 4
6 NULL NULL
Looks like you are trying to pivot without aggregation? Here is another option:
select A, B, C from
( select col1, col2, dense_rank() over (partition by col1 order by col2) dr from #table) t
pivot
( max(t.col2) for t.col1 in (A, B, C)) pvt;
Also check this out for more examples/discussion: TSQL Pivot without aggregate function

How to count distinct rows and get data of the row and count of it as a second column

Let's say I have a data
ID
AAA
ABB
ABC
BDS
BRD
CXD
DCU
ETS
I would like to count distinct to a first letter rows and get the number of their appearance to the right. Sorry I know I am not a very good user of a technical language, but I am new to SQL and English is not my first language.
So by script I would like to return
ID Total
A 3
B 2
C 1
D 1
E 1
I have tried
select left(id,1), count(left(id,1) as Total
from Places
group by Id
order by Total desc;
, but it didn't work. Your help will be greatly appreciated.
select left(id,1), count(*) as Total
from Places
group by left(id,1)
order by Total desc;
Is this you need?
declare #t table(val varchar(10))
insert into #t
select 'AAA' union all
select 'ABB' union all
select 'ABC' union all
select 'BDS' union all
select 'BRD' union all
select 'CXD' union all
select 'DCU' union all
select 'ETS'
select left(t1.val,1) as id ,count(t1.val) as total from #t as t1 left join
(
select distinct right(val,1) as val from #t
) as t2 on t1.val =t2.val
group by left(t1.val,1)
Result is
id total
---- -----------
A 3
B 2
C 1
D 1
E 1

How to comapre two columns of a table in sql?

In a table there are two columns:
-----------
| A | B |
-----------
| 1 | 5 |
| 2 | 1 |
| 3 | 2 |
| 4 | 1 |
-----------
Want a table where if A=B then
-------------------
|Match | notMatch|
-------------------
| 1 | 5 |
| 2 | 3 |
| Null | 4 |
-------------------
How can i do this?
I tried something which shows the Matched part
select distinct C.A as A from Table c inner join Table d on c.A=d.B
Try this:
;WITH TempTable(A, B) AS(
SELECT 1, 5 UNION ALL
SELECT 2, 1 UNION ALL
SELECT 3, 2 UNION ALL
SELECT 4, 1
)
,CTE(Val) AS(
SELECT A FROM TempTable UNION ALL
SELECT B FROM TempTable
)
,Match AS(
SELECT
Rn = ROW_NUMBER() OVER(ORDER BY Val),
Val
FROM CTE c
GROUP BY Val
HAVING COUNT(Val) > 1
)
,NotMatch AS(
SELECT
Rn = ROW_NUMBER() OVER(ORDER BY Val),
Val
FROM CTE c
GROUP BY Val
HAVING COUNT(Val) = 1
)
SELECT
Match = m.Val,
NotMatch= n.Val
FROM Match m
FULL JOIN NotMatch n
ON n.Rn = m.Rn
Try with EXCEPT, MINUS and INTERSECT Statements.
like this:
SELECT A FROM TABLE1 INTERSECT SELECT B FROM TABLE1;
You might want this:
SELECT DISTINCT
C.A as A
FROM
Table c
LEFT OUTER JOIN
Table d
ON
c.A=d.B
WHERE
d.ID IS NULL
Please Note that I use d.ID as an example because I don't see your schema. An alternate is to explicitly state all d.columns IS NULL in WHERE clause.
Your requirement is kind of - let's call it - interesting. Here is a way to solve it using pivot. Personally I would have chosen a different table structure and another way to select data:
Test data:
DECLARE #t table(A TINYINT, B TINYINT)
INSERT #t values
(1,5),(2,1),
(3,2),(4,1)
Query:
;WITH B AS
(
( SELECT A FROM #t
EXCEPT
SELECT B FROM #t)
UNION ALL
( SELECT B FROM #t
EXCEPT
SELECT A FROM #t)
), A AS
(
SELECT A val
FROM #t
INTERSECT
SELECT B
FROM #t
), combine as
(
SELECT val, 'A' col, row_number() over (order by (select 1)) rn FROM A
UNION ALL
SELECT A, 'B' col, row_number() over (order by (select 1)) rn
FROM B
)
SELECT [A], [B]
FROM combine
PIVOT (MAX(val) FOR [col] IN ([A], [B])) AS pvt
Result:
A B
1 3
2 4
NULL 5

query to join two tables using a primary key and comma separate the results

Suppose there are two tables-
T1 T2
--- ----------
A A | 1
A A | 2
A A | 3
How to write a query that returns the results as following
Col1 Col2
---- -----
A 1,2,3
(One row)
Thanks in advance
Declare #tempA Table
(Val Char(1))
DECLARE #tempB Table
(Val Char(1),
ID INT)
INSERT INTO #tempA
SELECT 'A'
UNION
SELECT 'A'
UNION
SELECT 'A'
INSERT INTO #tempB
SELECT 'A',1
UNION
SELECT 'A',2
UNION
SELECT 'A',3
SELECT DISTINCT Val,STUFF((SELECT ','+CONVERT(VARCHAR,ID)as 'data()'
FROM #tempB B
Where B.Val = A.Val
FOR XML PATH('') ),1,1,'')
FROM #tempA A
Hope this will solve.