Group by SQL with count - sql

Lets say we have got rows like that:
MyTable
ID Name Product
------------------
1 Adam x
2 Adam y
3 Adam z
4 Peter a
5 Peter b
Using query like:
Select Name, Count(Product) from MyTable
group by Name
results will be:
Adam 3
Peter 2
But I would like results like:
1 Adam x 3
2 Adam y 3
3 Adam z 3
4 Peter a 2
5 Peter b 2
I hope Ypu know what I mean
Could You help me with that query,
thanks for help,
Bye

You can join the table with a subquery run on the table to select the counts:
SELECT a.ID as ID, a.Name as Name, a.Product as Product, ISNULL(b.cnt,0) as Cnt
FROM MyTable a
LEFT JOIN (SELECT Name, COUNT(*) as Cnt FROM MyTable GROUP BY Name) b
ON a.Name = b.Name

How about?
Select *, Count() OVER(PARTITION BY Name) As C
from MyTable

Select a.Id,
a.Name,
a.Product,
IsNull(b.CountOfUsers,0) as CountOfUsers
From MyTable a
Left Join (Select Name, Count(Product) as CountOfUsers from MyTable
group by Name)b on a.Name = b.Name

;WITH c AS (SELECT Name, COUNT(Product) CountOfProduct
FROM MyTable
GROUP BY Name)
SELECT t.Id, t.Name, t.Product, c.CountOfProduct
FROM MyTable t
INNER JOIN c ON c.Name = t.Name

Use:
SELECT x.id,
x.name,
x.product,
COALESCE(y.name_count, 0) AS num_instances
FROM MyTable x
LEFT JOIN (SELECT t.name,
COUNT(*) AS name_count
FROM MyTable t
GROUP BY t.name) y ON y.name = x.name
COALESCE is the ANSI standard means of handling NULL values, and is supported by MySQL, SQL Server, Oracle, Postgre, etc.

Related

Select the duplicate rows with specific values

How can I only get the data with the same ID, but not the same Name?
The following is the example to explain my thought. Thanks.
ID Name Date
123 Amy 08/03/2022
123 Amy 12/03/2022
456 Billy 08/03/2022
456 Cat 09/03/2022
789 Peter 10/03/2022
Expected Output:
ID Name Date
456 Billy 08/03/2022
456 Cat 09/03/2022
How I have done.
select ID, Name, count(*)
from table
groupby ID, Name
having count(*) > 1
But the result included the following parts that I do not want it.
ID Name Date
123 Amy 08/03/2022
123 Amy 12/03/2022
One approach would be to use a subquery to identify IDs that have multiple names.
SELECT *
FROM YourTable
WHERE ID IN (SELECT ID FROM YourTable GROUP BY ID HAVING COUNT(DISTINCT Name) > 1)
I'd join the table to its self like this:
SELECT DISTINCT
a.Id as ID_A,
b.Id as ID_B,
a.[Name] as Name_A
FROM
Test as a
INNER JOIN Test as b
ON A.Id = B.Id
WHERE
A.[Name] <> B.[Name]
Do you want
SELECT * FROM table_name
WHERE ID = 456;
or
SELECT * FROM table_name
WHERE ID IN
(SELECT
ID
FROM table_name
GROUP BY ID
HAVING COUNT(DISTINCT name) > 1
);
?
Window functions are likely to be the most efficient here. They do not require self-joining of the source table.
Unfortunately, SQL Server does not support COUNT(DISTINCT as a window function. But we can simulate it by using DENSE_RANK and MAX
WITH DistinctRanks AS (
SELECT *,
rnk = DENSE_RANK(*) OVER (PARTITION BY ID ORDER BY Name)
FROM YourTable
),
MaxRanks AS (
SELECT *,
mr = MAX(rnk) OVER (PARTITION BY ID)
FROM DistinctRanks
)
SELECT
ID,
Name,
Count
FROM MaxRanks t
WHERE t.mr > 1;

Count similar values from table by combining two tables

I have two table
table A
name id
ABC 1
PQR 2
XYZ 1
QWE 2
DFG 3
Another table
table B
id idname
1 stuart
2 bob
3 alex
expected output
id idname count
1 stuart 2
2 bob 2
3 alex 1
Iam using oracle 9i, Is it possible to obtain the expected result?
I have tried using distinct keyword but its not helping as it provides only the total count
That's simple. Join and count:
select b.id,
b.idname,
count(*) as cnt
from table_a a
join table_b b on a.id = b.id
group by b.id, b.idname;
If you need all the record from table b even if there is no corresponding row in table a, you can use an outer join:
select b.id,
b.idname,
count(a.id) as cnt
from table_a a
right join table_b b on a.id = b.id
group by b.id, b.idname;
Same can be achieved by using a left join:
select b.id,
b.idname,
count(a.id) as cnt
from table_b b
left join table_a a on a.id = b.id
group by b.id, b.idname;
Use JOIN to get data from both tables and use the aggregate function COUNT with GROUP BY.
Query
select t1.id, t1.idname, count(t2.name) as count
from TableB t1
left join TableA t2
on t1.id = t2.id
group by t1.id, t1.idname
order by count(t2.name) desc, t1.id;;

Selecting one of the duplicate rows based on one column

I've been trying to filter the duplicate rows out based on one column.
SELECT DISTINCT
a.id,
b.name,
b.number
FROM
b
LEFT JOIN a
ON a.name = b.name
ORDER BY
b.name
ASC;
And the result is:
id,name,number
1 Bob NULL
1 Bob 100
2 Bob NULL
2 Bob 200
3 Bob NULL
3 Bob 300
4 Bob 400
I'm trying to achieve the result like this:
id,name,number
1 Bob 100
2 Bob 200
3 Bob 300
4 Bob 400
If you want to combine the NULL values, I would suggest aggregation:
SELECT a.id, b.name, SUM(b.number) as number
FROM b LEFT JOIN
a
ON a.name = b.name
GROUP BY a.id, b.name;
Alternatively, just use a WHERE clause:
SELECT a.id, b.name, b.number
FROM b LEFT JOIN
a
ON a.name = b.name
WHERE b.name IS NOT NULL;
You can use ROW_NUMBER in order to selectively filter out NULL values in b.number field:
SELECT id, name, number
FROM (
SELECT a.id, b.name, b.number,
ROW_NUMBER() OVER (ORDER BY COALESCE(b.number, -1) DESC) AS rn
FROM b
LEFT JOIN a ON a.name = b.name) AS t
WHERE t.rn = 1
ORDER BY name ASC
The above query handles only cases with one, or at most two rows per id, with one of them, or both, being NULL.

Finding duplicate differences between two tables in sql

I try to find duplicate rows between two tables. This code works only if records are not duplicated:
(select [Name], [Age] from PeopleA
except
select [Name], [Age] from PeopleB)
union all
(select [Name], [Age] from PeopleB
except
select [Name], [Age] from PeopleA)
How to find missing, duplicate records. Robert 34 in PersonA table for example below:
PersonA:
Name | Age
-------------
John | 45
Robert | 34
Adam | 26
Robert | 34
PersonB:
Name | Age
-------------
John | 45
Robert | 34
Adam | 26
You can use UNION ALL to concat both tables and Group By with Having clause to find duplicates:
SELECT x.Name, x.Age, Cnt = Count(*)
FROM (
SELECT a.Name, a.Age
FROM PersonA a
UNION ALL
SELECT b.Name, b.Age
FROM PersonB b
) x
GROUP BY x.Name, x.Age
HAVING COUNT(*) > 1
According to your clarification in the comment, you could use following query to find all name-age combinations in PersonA which are different in PersonB:
WITH A AS(
SELECT a.Name, a.Age, cnt = count(*)
FROM PersonA a
GROUP BY a.Name, a.Age
),
B AS(
SELECT b.Name, b.Age, cnt = count(*)
FROM PersonB b
GROUP BY b.Name, b.Age
)
SELECT a.Name, a.Age
FROM A a LEFT OUTER JOIN B b
ON a.Name = b.Name AND a.Age = b.Age
WHERE a.cnt <> ISNULL(b.cnt, 0)
Demo
If you also want to find persons which are in PersonB but not in PersonA you should use a FULL OUTER JOIN as Gordon Linoff has commented:
WITH A AS(
SELECT a.Name, a.Age, cnt = count(*)
FROM PersonA a
GROUP BY a.Name, a.Age
),
B AS(
SELECT b.Name, b.Age, cnt = count(*)
FROM PersonB b
GROUP BY b.Name, b.Age
)
SELECT Name = ISNULL(a.Name, b.Name), Age = ISNULL(a.Age, b.Age)
FROM A a FULL OUTER JOIN B b
ON a.Name = b.Name AND a.Age = b.Age
WHERE ISNULL(a.cnt, 0) <> ISNULL(b.cnt, 0)
Demo
I like Tim's answer but you need to check in both tables if the records are missing. He is only checking if the records are missing in table A. Try this to check if records are missing in either of the tables and how many times.
Select *, 'PersonB' MissingInTable, a.cnt - isnull(b.cnt,0) TimesMissing From
(
Select *, count(1) cnt from PersonA group by Name, Age) A Left join
(Select *, count(1) cnt from PersonB group by Name, Age) B
On a.age=b.age and a.name=b.name
where a.cnt>isnull(b.cnt,0)
Union All
Select *, 'PersonA' MissingInTable, b.cnt - isnull(a.cnt,0) TimesMissing From
(
Select *, count(1) cnt from PersonA group by Name, Age) A Right join
(Select *, count(1) cnt from PersonB group by Name, Age) B
On a.age=b.age and a.name=b.name
where b.cnt>isnull(a.cnt,0)
See demo here : http://sqlfiddle.com/#!6/06020/13
Add another UNION ALL!
Code:
(SELECT [Name], [Age], 'Missing from B' AS [Type] from PeopleA
EXCEPT
SELECT [Name], [Age], 'Missing from B' AS [Type] from PeopleB)
UNION ALL
(SELECT [Name], [Age], 'Missing from A' as [Type] from PeopleB
EXCEPT
SELECT [Name], [Age], 'Missing from A' AS [Type] from PeopleA)
UNION ALL
SELECT [Name], [Age], 'Duplicate' AS [Type] FROM PeopleA INNER JOIN PeopleB ON PeopleA.Name = PeopleB.Name AND
PeopleA.Age=PeopleB.Age

T-SQL using SUM for a running total

I have a simple table with some dummy data setup like:
|id|user|value|
---------------
1 John 2
2 Ted 1
3 John 4
4 Ted 2
I can select a running total by executing the following sql(MSSQL 2008) statement:
SELECT a.id, a.user, a.value, SUM(b.value) AS total
FROM table a INNER JOIN table b
ON a.id >= b.id
AND a.user = b.user
GROUP BY a.id, a.user, a.value
ORDER BY a.id
This will give me results like:
|id|user|value|total|
---------------------
1 John 2 2
3 John 4 6
2 Ted 1 1
4 Ted 2 3
Now is it possible to only retrieve the most recent rows for each user? So the result would be:
|id|user|value|total|
---------------------
3 John 4 6
4 Ted 2 3
Am I going about this the right way? any suggestions or a new path to follow would be great!
No join is needed, you can speed up the query this way:
select id, [user], value, total
from
(
select id, [user], value,
row_number() over (partition by [user] order by id desc) rn,
sum(value) over (partition by [user]) total
from users
) a
where rn = 1
try this:
;with cte as
(SELECT a.id, a.[user], a.value, SUM(b.value) AS total
FROM users a INNER JOIN users b
ON a.id >= b.id
AND a.[user] = b.[user]
GROUP BY a.id, a.[user], a.value
),
cte1 as (select *,ROW_NUMBER() over (partition by [user]
order by total desc) as row_num
from cte)
select id,[user],value,total from cte1 where row_num=1
SQL Fiddle Demo
add where statement:
select * from
(
your select statement
) t
where t.id in (select max(id) from table group by user)
also you can use this query:
SELECT a.id, a.user, a.value,
(select max(b.value) from table b where b.user=a.user) AS total
FROM table a
where a.id in (select max(id) from table group by user)
ORDER BY a.id
Adding a right join would perform better than nested select.
Or even simpler:
SELECT MAX(id), [user], MAX(value), SUM(value)
FROM table
GROUP BY [user]
Compatible with SQL Server 2008 or later
DECLARE #AnotherTbl TABLE
(
id INT
, somedate DATE
, somevalue DECIMAL(18, 4)
, runningtotal DECIMAL(18, 4)
)
INSERT INTO #AnotherTbl
(
id
, somedate
, somevalue
, runningtotal
)
SELECT LEDGER_ID
, LL.LEDGER_DocDate
, LL.LEDGER_Amount
, NULL
FROM ACC_Ledger LL
ORDER BY LL.LEDGER_DocDate
DECLARE #RunningTotal DECIMAL(18, 4)
SET #RunningTotal = 0
UPDATE #AnotherTbl
SET #RunningTotal=runningtotal = #RunningTotal + somevalue
FROM #AnotherTbl
SELECT *
FROM #AnotherTbl