Filter data based on multiple rows SQL - sql

This is probably as simple SQL query. I'm finding it little tricky, as it's been a while I've written SQL.
ID NAME VALUE
--- ------ -------
1 Country Brazil
1 Country India
2 Country US
2 EmpLevel 1
3 EmpLevel 3
Pseudo Query:
Select *
from table_name
where (country = US or country = Brazil)
and (Employee_level = 1 or Employee_level = 3)
This query should return
ID NAME VALUE
--- ------ -------
2 Country US
2 EmpLevel 1
(As record with ID - 2 has Country as 'US' and EmpLevel '1')
I went through couple SO posts as well.
Multiple row SQL Where clause
SQL subselect filtering based on multiple sub-rows
Evaluation of multiples 'IN' Expressions in 'WHERE' clauses in mysql

I assume you're expected results for the country should be US instead of Brazil. Here's one option using a join with conditional aggregation:
select y.*
from yourtable y join (
select id
from yourtable
group by id
having max(case when name = 'Country' then value end) in ('US','Brazil') and
max(case when name = 'EmpLevel' then value end) in ('1','3')
) y2 on y.id = y2.id
SQL Fiddle Demo

Related

SQLite query to get table based on values of another table

I am not sure what title has to be here to correctly reflect my question, I can only describe what I want.
There is a table with fields:
id, name, city
There are next rows:
1 John London
2 Mary Paris
3 John Paris
4 Samy London
I want to get a such result:
London Paris
Total 2 2
John 1 1
Mary 0 1
Samy 1 0
So, I need to take all unique values of name and find an appropriate quantity for unique values of another field (city)
Also I want to get a total quantity of each city
Simple way to do it is:
1)Get a list of unique names
SELECT DISTINCT name FROM table
2)Get a list of unique cities
SELECT DISTINCT city FROM table
3)Create a query for every name and city
SELECT COUNT(city) FROM table WHERE name = some_name AND city = some_city
4)Get total:
SELECT COUNT(city) FROM table WHERE name = some_name
(I did't test these queries, so maybe there are some errors here but it's only to show the idea)
As there are 3 names and 2 cities -> 3 * 2 = 6 queries to DB
But for a table with 100 cities and 100 names -> 100 * 100 = 10 000 queries to DB
and it may take a lot of time to do.
Also, names and cities may be changed, so, I can't create a query with predefined names or cities as every day it's new ones, so, instead of London and Paris it may be Moscow, Turin and Berlin. The same thing with names.
How to get such table with one-two queries to original table using sqlite?
(sqlite: I do it for android)
You can get the per-name results with conditional aggregation. As for the total, unfortunately SQLite does not support the with rollup clause, that would generate it automatically.
One workaround is union all and an additional column for ordering:
select name, london, paris
from (
select name, sum(city = 'London') london, sum(city = 'Paris') paris, 1 prio
from mytable
group by name
union all
select 'Total', sum(city = 'London'), sum(city = 'Paris'), 0
from mytable
) t
order by prio, name
Actually the subquery might not be necessary:
select name, sum(city = 'London') london, sum(city = 'Paris') paris, 1 prio
from mytable
group by name
union all
select 'Total', sum(city = 'London'), sum(city = 'Paris'), 0
from mytable
order by prio, name
#GMB gave me the idea of using group by, but as I do it for SQLite on Android, so, the answer looks like:
SELECT name,
COUNT(CASE WHEN city = :london THEN 1 END) as countLondon,
COUNT(CASE WHEN city = :paris THEN 1 END) as countParis
FROM table2 GROUP BY name
where :london and :paris are passed params, and countLondon and countParis are fields of the response class

How to use multiple counts in where clause to compare data of a table in sql?

I want to compare data of a table with its other records. The count of rows with a specific condition has to match the count of rows without the where clause but on the same grouping.
Below is the table
-------------
id name time status
1 John 10 C
2 Alex 10 R
3 Dan 10 C
4 Tim 11 C
5 Tom 11 C
Output should be time = 11 as the count for grouping on time column is different when a where clause is added on status = 'C'
SELECT q1.time
FROM (SELECT time,
Count(id)
FROM table
GROUP BY time) AS q1
INNER JOIN (SELECT time,
Count(id)
FROM table
WHERE status = 'C'
GROUP BY time) AS q2
ON q1.time = q2.time
WHERE q1.count = q2.count
This is giving the desired output but is there a better and efficient way to get the desired result?
Are you looking for this :
select t.*
from table t
where not exists (select 1 from table t1 where t1.time = t.time and t1.status <> 'C');
However you can do :
select time
from table t
group by time
having sum (case when status <> 'c' then 1 else 0 end ) = 0;
If you want the times where the rows all satisfy the where clause, then in Postgres, you can express this as:
select time
from t
group by time
having count(*) = count(*) filter (where status = 'C');

Getting sum of 2 columns each with unique condition

I've two tables like this.
create table teams (
"ID" Integer NOT NULL ,
"STADIUM_ID" Integer NOT NULL ,
"NAME" Varchar2 (50) NOT NULL ,
primary key ("ID")
) ;
create table matches (
"ID" Integer NOT NULL ,
"WINNER_ID" Integer NOT NULL ,
"OPPONENT_ID" Integer NOT NULL ,
"WINNERSCORE" Integer,
"OPPONENTSCORE" Integer,
primary key ("ID","WINNER_ID","OPPONENT_ID")
) ;
They have the following data :
select * from matches;
ID WINNER_ID OPPONENT_ID WINNERSCORE OPPONENTSCORE
---------- ---------- ----------- ----------- -------------
1 5 2 5 2
2 4 5 1 0
3 3 2 1 0
4 3 2 1 0
5 1 2 2 0
6 3 1 2 1
select * from teams;
ID STADIUM_ID NAME
---------- ---------- -----------
1 1 Team1
2 3 Team2
3 4 Team3
4 2 Team4
5 5 Team5
I need to get the sum of the goals for each team.
For this aim, tried the following query and got the results below :
select name,
(select sum(opponentscore)
from matches
where opponent_id = teams.id) +
(select sum(winnerscore) from matches where winner_id = teams.id) sum
from teams;
NAME SUM
-------------------------------------------------- ----------
Team1 3
Team2
Team3
Team4
Team5 5
Do you have any suggestion ?
All you need is to calculate seperately opponentscore and winnerscore by each individual team, and combine them with UNION ALL :
select name, sum(score) total_score
from
(
select name, sum(winnerscore) score
from teams t join matches m on ( t.id = m.winner_id )
group by name
union all
select name, sum(opponentscore) score
from teams t join matches m on ( t.id = m.opponent_id )
group by name
)
group by name
order by 1;
SQL Fiddle Demo
you should use join and group by
select name, sum(matches.opponentscore) + sum(matches.winnerscore) my_sum
from matches
inner join teams on teams.id = matches.winner_id
group by teams.name
You could join table teams twice with table matches:
SELECT name, SUM(wonMatches.WINNERSCORE + lostMatches.OPPONENTSCORE) as goals
FROM (teams INNER JOIN matches as wonMatches ON teams.ID = wonMatches.WINNER_ID)
INNER JOIN matches as lostMatches ON teams.ID = lostMatches.OPPONENT_ID
GROUP BY name
My solution is : change your database schema. Restart thinking your app's requirements. This schema does not answer the value your user are expecting from you.
From what I see, I would say that you're trying to build an app for fans that want to track their team / favorite player progress so they can brag.
That being said, I would have, at the end, those tables :
fan
team (id_team)
player (id_player, id_team)
tournament (id_tournament)
match (id_match, id_tournament, start_on, id_team_home, id_team_visitor)
goals (id_match, id_player, goaled_on)
So now, I believe that writing your query would be much more simple. You'll just have to join team, player, count over goals and group by team.
The problem is with NULLs - the subqueries return NULL when no result is found, and NULL + anything == NULL.
Most straightforward fix is:
select name,
nvl(
(select sum(opponentscore) from matches where opponent_id = teams.id),
0
)
+
nvl(
(select sum(winnerscore) from matches where winner_id = teams.id),
0
) sum
from teams;
For performance reasons tohugh, you might want to consider using a joined query with GROUP BY as suggested by others.

sql combining 2 queries with different order by group by

I have a query where I am counting the most frequent response in a database and ranking them by highest amount so using group by and order by.
The following shows how to do it for one:
select health, count(health) as count
from [Health].[Questionaire]
group by Health
order by count(Health) desc
which outputs the following:
Health Count
----------- -----
Very Good 6
Good 5
Poor 4
I would like to do with another column on the same table another query similar to the following so two queries using one sql statement like the following:
Health Count Diet Count
----------- ----- ----- -----
Very Good 6 Very Good 6
Good 5 Good 4
Poor 4 Poor 3
UPDATE!!
Hello this is how the table looks like at the moment
ID Diet Health
----------- ----- -------
101 Very Good Very Good
102 Poor Good
103 Poor Poor
I would like to do with another column on the same table another query similar to the following so two queries using one sql statement like the following:
Health Count Diet Count
----------- ----- ----- -----
Very Good 2 Very Good 1
Poor 1 Good 1
Good 0 Poor 1
Can anyone please help me out with this one?
Can provide further clarification if needed!
Here are 2 different ways of doing it, notice i removed the redundant column:
Test data:
DECLARE #t table(Health varchar(20), Diet varchar(20))
INSERT #t values
('Very good', 'Very good'),
('Poor', 'Good'),
('Poor', 'Poor')
Query 1:
;WITH CTE1 as
(
SELECT Health, count(*) CountHealth
FROM #t --[Health].[Questionaire]
GROUP BY health
), CTE2 as
(
SELECT Diet, count(*) CountDiet
FROM #t --[Health].[Questionaire]
GROUP BY Diet
)
SELECT
coalesce(Health, Diet) Grade,
coalesce(CountHealth, 0) CountHealth,
coalesce(CountDiet, 0) CountDiet
FROM CTE1
FULL JOIN
CTE2
ON CTE1.Health = CTE2.Diet
ORDER BY CountHealth DESC
Result 1:
Grade CountHealth CountDiet
Poor 2 1
Very good 1 1
Good 0 1
Mixing the results like that is really not good practice, so here is a different solution
Query 2:
SELECT Health, count(*) Count, 'Health' Grade
FROM #t --[Health].[Questionaire]
GROUP BY health
UNION ALL
SELECT Diet, count(*) CountDiet, 'Diet'
FROM #t --[Health].[Questionaire]
GROUP BY Diet
ORDER BY Grade, Count DESC
Result 2:
Health Count Grade
Good 1 Diet
Poor 1 Diet
Very good 1 Diet
Poor 2 Health
Very good 1 Health
You need to join the table to itself, but (as your sample data shows) to deal with gaps in actual data for specific values.
If you have a table that has the range of health/diet values:
select
v.value Status,
count(a.id) healthCount,
count(b.id) DietCount
from health_diet_values v
left join Questionaire a on a.health = v.value
left join Questionaire b on b.diet = v.value
group by v.value
or if you don't have such a table, you need to generate the list of values manually and join from that:
select
v.value Status,
count(a.id) healthCount,
count(b.id) DietCount
from (select 'Very Good' value union all
select 'Good' union all
select 'Poor') v
left join Questionaire a on a.health = v.value
left join Questionaire b on b.diet = v.value
group by v.value
Both of these queries produce zeroes if there is no matching data for the value.
Note that in your desired output you have a redundant column - you repeat the value column. The above queries produce output that looks like:
Status HealthCount DietCount
-------------------------------
Very Good 2 1
Good 1 1
Poor 0 1

Counting values in columns

What I am looking for is to group by and count the total of different data in the same table and have them show in two different columns. Like below.
Data in table A
Fields:
Name Type
Bob 1
John 2
Bob 1
Steve 1
John 1
Bob 2
Desired result from query:
Name Type 1 Type 2
Bob 2 1
John 1 1
Steve 1 0
This will do the trick in SQL Server:
SELECT
name,
SUM( CASE type WHEN 1 THEN 1 ELSE 0 END) AS type1,
SUM( CASE type WHEN 2 THEN 1 ELSE 0 END) AS type2
FROM
myTable
GROUP BY
name
No time to write the code, but the Case statement is what you want here. SImply havea value of 1 if it meets the case and zero if it deosn't. Then you can sum the columns.
Use two separate GROUP BY subqueries.
SELECT Name, a.Count1, b.Count2
from myTable
JOIN
(SELECT Name, SUM(Type) AS Count1 FROM myTable GROUP BY Name WHERE Type=1) AS a ON a.Name = myTable.Name
(SELECT Name, SUM(Type) FROM myTable GROUP BY Name WHERE Type=2) AS b ON b.Name = myTable.Name
You're looking for a CrossTab solution. The above solutions will work, but you'll come unstuck if you want a general solution and have N types.
A CrossTab solution will solve this for you. If this is for quickly crunching some numbers then dump your data into Excel and use the native Pivot Table feature.
If it's for a RDBMS in an app, then it depends upon the RDBMS. MS SQL 2005 and above has a crosstab syntax. See:
http://www.databasejournal.com/features/mssql/article.php/3521101/Cross-Tab-reports-in-SQL-Server-2005.htm
#Seb has a good solution, but it's server-dependent. Here's an alternate using subselects that should be portable:
select
name,
(select count(type) from myTable where type=1 and name=a.name) as type1,
(select count(type) from myTable where type=2 and name=a.name) as type2
from
myTable as a
group by
name