Postgres SQL GROUP BY depending on a value from another column - sql

I am trying to query set result set which returns something like the below.
I need to return only 1 row per name and need to GROUP BY but only the name that have a value of '8' under the Grade column are desired. The below is a result from another query. Because Sandra has a different value other than 8, Sandra should be omitted.
eg:- In the below I need to get one row for John only.
Please advise. Thank you.
Name Grade
======= =====
Sandra 8
Sandra 8
Sandra 8
Sandra 9
John 8
John 8
John 8
John 8
Expected Result - 1 row
Name Grade
John 8

Aggregate your table on the name, and then use a HAVING clause to filter out names which have a grade other than 8 (or any other values which you do not want).
SELECT name, MIN(grade) AS grade
FROM yourTable
GROUP BY name
HAVING SUM(CASE WHEN grade <> 8 THEN 1 ELSE 0 END) = 0;
Demo
Update:
If the grade column were text, and you wanted to compare against the string '8' instead of a number, then you could use this HAVING clause:
HAVING SUM(CASE WHEN grade <> '8' THEN 1 ELSE 0 END) = 0;

If you want names that have only 8s, you can do:
select name
from t
group by name
having min(grade) = max(grade) and min(grade) = 8;

Alternately,
SELECT DISTINCT B.name, B.grade
FROM
(
SELECT name FROM yourTable GROUP BY name HAVING COUNT(DISTINCT grade) = 1
) Z
INNER JOIN
yourTable B
ON Z.name = B.name
AND B.grade = 8;

Related

LOOP through column to replace same value with different names SQL

I have a table like this
id
dept
1
a
1
b
1
b
2
a
2
b
And I'd like to rename values like this
id
dept
1
a
1
b1
1
b2
2
a
2
b1
All values in dept are grouped by id, same departments repeat each group. I'm unsure how to make a loop that does one pass per id (replace 'b' with 'b1'), then a second pass (replace 'b' with 'b2').
Or does once per table, but each statement ends after one replace per id.
Thanks in advance!
We don't need to loop. We can just use row_number().
select id
,case when dept = 'b' then dept||row_number() over(partition by id, dept) else dept end as dept
from t
id
dept
1
a
1
b1
1
b2
2
a
2
b1
Fiddle

Sort by aggregate value in sql

Let's say I have the following student data:
classroom gender student_id
-------------------------------------
First Grade M 123
First Grade F 124
First Grade F 125
Second Grade M 126
Third Grade M 127
...
I want to produce the following result: top 3 biggest classrooms ordered by total number of students with detail for each:
classroom boys_count girls_count total_count
--------------------------------------------------
Third Grade 30 30 60
First Grade 20 5 25
Fourth Grade 10 10 20
How can I do that in sql ? If necessary, I can use specific postrges features.
What I tried so far:
SELECT count(*) as total_count
, gender
, classroom
ORDER BY 1
GROUP BY classroom, gender
LIMIT 3
Then I re-organise results in some scripting language. But this is too slow. I want to have the correct results with one query
select classroom as name,
sum(case when gender = 'M' then 1 else 0 end) as boys_count,
sum(case when gender = 'F' then 1 else 0 end) as girls_count,
count(*) as total_count
from your_table
group by classroom
order by count(*) desc
limit 3

Select from Column A based on Column B

I have 4 tables joined together and this is the result of that query,
Name Year
Erin 2015
Erin NULL
Erin NULL
Sarah 2010
Peter 2011
The two columns are from two different tables. They both have an ID I can match. How do I narrow it down to this result,
Name Year
Erin 2015
Erin NULL
Erin NULL
When Year = 2015, I want all of Erin's.
SELECT Name
CASE WHEN Year = 2015 THEN
......
END
Rest of the columns I've selected
FROM Table
Joined with 4 other tables
WHERE
Stuff
What can I put in between the CASE statement.
It sounds like you want to see all the records from any name that has at least one record with a year = '2015'. If so, then:
SELECT Name, Year, Other_Columns
FROM Name N
JOIN YEAR Y on Y.ID = N.ID
WHERE N.ID IN (
SELECT ID
FROM YEAR
WHERE YEAR = '2015'
)
Put the same case when into the where clause:
select
case when .. then 'a' else 'b' end
from TABLE
where
(case when .. then 'a' else 'b' end) = 'b'
;

How to declare a row as a Alternate Row

id Name claim priority
1 yatin 70 5
6 yatin 1 10
2 hiren 30 3
3 pankaj 40 2
4 kavin 50 1
5 jigo 10 4
7 jigo 1 10
this is my table and i want to arrange this table as shown below
id Name claim priority AlternateFlag
1 yatin 70 5 0
6 yatin 1 10 0
2 hiren 30 3 1
3 pankaj 40 2 0
4 kavin 50 1 1
5 jigo 10 4 0
7 jigo 1 10 0
It is sorted as alternate group of same row.
I am Using sql server 2005. Alternate flag starts with '0'. In my example First record with name "yatin" so set AlternateFlag as '0'.
Now second record has a same name as "yatin" so alternate flag would be '0'
Now Third record with name "hiren" is single record, so assign '1' to it
In short i want identify alternate group with same name...
Hope you understand my problem
Thanks in advance
Try
SELECT t.*, f.AlternateFlag
FROM tbl t
JOIN (
SELECT [name],
AlternateFlag = ~CAST(ROW_NUMBER() OVER(ORDER BY MIN(ID)) % 2 AS BIT)
FROM tbl
GROUP BY name
) f ON f.name = t.name
demo
You could use probably an aggregate function COUNT() and then HAVING() and then UNION both Table, like:
SELECT id, A.Name, Claim, Priority, 0 as AlternateFlag
FROM YourTable
INNER JOIN (
SELECT Name, COUNT(*) as NameCount
FROM YourTable
GROUP BY Name
HAVING COUNT(*) > 1 ) A
ON YourTable.Name = A.Name
UNION ALL
SELECT id, B.Name, Claim, Priority, 1 as AlternateFlag
FROM YourTable
INNER JOIN (
SELECT Name, COUNT(*) as NameCount
FROM YourTable
GROUP BY Name
HAVING COUNT(*) = 1 ) B
ON YourTable.Name = B.Name
Now, this assumes that the Names are unique meaning the names like Yatin for example although has two counts is only associated to one person.
See my SqlFiddle Demo
You can use Row_Number() function with OVER that will give you enumeration, than use the reminder of integer division it by 2 - so you'll get 1s and 0s in your SELECT or in the view.

Sql Result two columns

I have the following table:
Full name status
ricardo 1 2
ricardo 2 4
How do I make a select to return like this:
name totalstatus1 totalstatus2 total
ricardo 2 4 6
You did not include the name of the column with the 2 and 4 but you could use something similar to this:
select name,
sum(case when status = 1 then value end) totalStatus1,
sum(case when status = 2 then value end) totalStatus2,
sum(value) Total
from yourtable
group by name;
See SQL Fiddle with Demo