SQL: Add number of unique values in a group - sql

I have a table such as:
date
id
value
2020/4/4
1
a
2020/4/4
1
a
2020/4/4
1
b
2020/4/4
2
t
2020/4/4
2
u
2020/5/4
3
u
I want to find out how many IDs have more than one unique value at a particular date.
So this is what I should get for the table from above:
2020/4/4: 1 (=> only ID=1 has more than one unique value (a+b))
2020/4/5: 0
I tried to get it with:
SELECT date, SUM(CASE WHEN COUNT(DISTINCT value)>1 THEN 1 ELSE 0 END)
FROM table
GROUP BY date, id
But it did not work. How do I do it right?

Some databases will let you count "tuples", allowing this...
SELECT
date,
CASE WHEN COUNT(DISTINCT (id, value)) > COUNT(*) THEN 1 ELSE 0 END)
FROM
table
GROUP BY
date
Otherwise your style of approach works, but you need to aggregate twice using sub-queries.
SELECT date, MAX(has_duplicate_values)
FROM
(
SELECT date, id, CASE WHEN COUNT(DISTINCT value) > COUNT(*) THEN 1 ELSE 0 END has_duplicate_values
FROM table
GROUP BY date, id
)
AS date_id_aggregate
GROUP BY date

modify your request as following:
SELECT date, CASE WHEN COUNT(DISTINCT value)>1 THEN 1 ELSE 0 END FROM `table` GROUP BY date

I want to find out how many IDs have more than one unique value at a particular date.
You can aggregate twice:
SELECT date,
SUM(CASE WHEN num > num_distinct_values THEN 1 ELSE 0 END) as num_ids_with_duplicates
FROM (SELECT date, id,
COUNT(DISTINCT value) as num_distinct_values,
COUNT(*) as num
FROM table
GROUP BY date, id
) di
GROUP BY date;

Related

Sum by Groups Before and after appear Number in column SQL

I have a group of records by id ordered by date ('Date'), which I want to sum up the amounts or, in 2 groups, 1 before any number appears ('Condition'), and another group after the first number,It doesn't matter if a 0 appears after a number, add before any number appears, and add after.
You can use conditional aggregation. For example:
select
id,
sum(case when s = 0 then amount else 0 end) as amount_before,
sum(case when s <> 0 then amount else 0 end) as amount_after
from (
select t.*,
sum(abs(condition)) over(partition by id order by date) as s
from t
) x
group by id

Optimal method to query number of times a user has an posted an entry

I have a table such as,
Column | Type
id integer
job_id integer
user_id integer
date_posted datetime
I want to write a query that breaks down the # of users that have posted their jobs once vs the number of users that have posted at least one job multiple times
with user_jobs as (
select user_id, job_id, count(distinct date_posted) as num_posted
from table
group by user_id, job_id
)
Select SUM(Case when avg_num_posted > 1 then 1 end) as posted_multiple_times,
SUM(Case when avg_num_posted = 1 then 1 end) as posted_once
FROM(
Select user_id, avg(num_posted) as avg_num_posted
from user_jobs
Group by user_id) t
this gives me the output, but was wondering if there was a more optimal method, thanks!
If I understand correctly, you can compare the number of distinct jobs to the number of job/date combinations. Let me assume that a single job is never posted on the same date twice. If that is the case:
Select sum(case when num_listings > num_jobs then 1 end) as posted_multiple_times,
sum(case when num_listings = num_jobs then 1 end) as posted_once
from (select user_id, count(*) as num_listings, count(distinct job_id) as num_jobs
from t
group by user_id
) u;
If jobs can be posted twice on one day and you don't want to count that as a duplicate, then one method is:
Select sum(case when num_listings > num_jobs then 1 end) as posted_multiple_times,
sum(case when num_listings = num_jobs then 1 end) as posted_once
from (select user_id, count(*) as num_listings,
count(distinct job_id) as num_jobs
from (select distinct user_id, job_id, date_posted from t) t
group by user_id
) u;

SQL change the specific row

In my table below, i want to change the placement of specific row.
For example,
ID Name Count
1 X 50
2 Y 30
3 other 25
4 Z 20
It is DESC ordered and i would like to see X,Y,Z orderly. Also, in total, 'other' should be counted. In other words, count should be 125.
You can use union all and add last row to the end.
Something like this:
select id, name,count from table where name<>other
union all
select 4 as id, "other"as name, 135 as count from table
order by 1
or if you want to sum it
select id, name,count from table where name<>other
union all
select 4 as id, 'other' as name, sum(count) as count from table
order by 1
You can put some logic in the order by clause:
select id, name, count
from table
order by case when id <> 3 then 1 else 2 end, id
This way, the first ordering criteria is "rows X, Y, Z first, then the other ones", then you order the groups the way you want, in your case either by id or by name will work.
You can find a working example here
TRY THIS :
SELECT ID,
Name,
CASE
WHEN Name = 'OTHER' THEN (SELECT SUM (COUNT) FROM YOUR_TABLE)
ELSE SUM (COUNT)
END
FROM YOUR_TABLE
GROUP BY Name
ORDER BY Name DESC
I think union all may be the simplest approach, but like this:
select id, name, count
from ((select id, name, count, 1 as ord
from t
where name in ('X', 'Y', 'Z')
) union all
(select 4, 'other', sum(count), 2 as ord
from t
)
) t
order by ord, name;

SQL to find rows with selected property values

I have a database view which gives me following result:
From this I want to select all the users which have groups 00113 and 00221.. so on. That is the users which have access to all those groups and not any one group.
I am a fan of using group by and having for these types of queries. One method for finding things in a list:
select id
from table
where group_number in ('00113', '00221')
group by id
having count(distinct group_number = '00113') = 2;
A more general method that allows you to get members of one list, excluding members from another:
select id
from table
group by id
having sum(case when group_number = '00113' then 1 else 0 end) > 0 and
sum(case when group_number = '00221' then 1 else 0 end) > 0;
select * from table
where ID not IN
( select distinct ID from Table
where GROUP_NUMBER not IN ('00221','00113')
) group by ID having count (distinct GROUP_NUMBER) = 2;
As ruudvan pointed out this can be directly done with only one IN
select * from table
where ID IN ('00221','00113')
group by ID having count (distinct GROUP_NUMBER) = 2;
The inner query gives the list of all IDs whose group_number is not present in the given list.
So you need to select all the IDs not present in the IDs given by inner query
Your intent is a bit ambiguous but if what you want is to return the user that for example have access to the groups 00113 and 00221 and no other groups then this would work.
One way to do this would be to use a conditional aggregation in the having clause of the group by:
select id from your_table
group by id
having sum(case
when group_number = '00113' then 1
when group_number = '00221' then 1
else -1 end
) = 2
Sample SQL Fiddle
select first_name, last_name, id from mytable where group_number = '00113'
INTERSECT
select first_name, last_name, id from mytable where group_number = '00221'

How to get minimum value from the SQL table?

I have a table as below
ID Date
1 Null
1 Null
1 Null
1 02/02/2012
1 02/03/2012
1 02/04/2012
1 02/05/2012
I want to take a min date from the above table, that's result should be Null
I was trying to write
select min(date), Id from Table group by ID
then result is 02/02/2012, but I want Null.
Is there any otherway to pull Null value from the above table except the below method?
select top 1 date, ID from table order by date asc
Assuming that your dbms is SQL-Server.
If you want to group by id but select all fields anyway, you can use a cte with ROW_NUMBER function:
WITH cte AS(
SELECT x.*
, RN=ROW_NUMBER()OVER(Partition By id Order By date)
FROM Table x
)
SELECT * FROM cte
WHERE RN=1
http://sqlfiddle.com/#!3/cc2a4/7
By default the functions MAX and MIN do not count NULL in their evaluation of your data.
Try in this way, should do the trick :
SELECT
CASE WHEN MIN(COALESCE(Date, '19001231')) = '19001231' THEN NULL ELSE MIN(Date) END AS Date,
Id
FROM X
group by ID
I think some of them are given the answer. but the final result working with below query
SELECT CASE WHEN MIN(coalesce(date, '1900-01-01')) = '1900-01-01' THEN NULL ELSE MIN(date) END AS Date, ID
FROM table
GROUP BY ID
You can also do a simple check for 'NULL'. I didn't return an "ID" in the example since the "ID" seems meaningless in the example schema/query given.
IF (EXISTS(SELECT TOP 1 * FROM x WHERE date IS NULL)) SELECT NULL AS MinDate
ELSE SELECT MIN(date) AS MinDate FROM x;
http://sqlfiddle.com/#!3/d5fca/11