Suppose I have a table with 2 columns (status and date) like the following:
status: U T U U L
date: 12 14 15 16 17
Can I (using only 1 SQL statement) count the number of distinct values in the status? That is:
count(U)=3
count(T)=1
count(L)=2
count(P)=0
Can I do this with 1 SQL query?
Note: I have static values in status. I can only have (U-T-L-P)
You need to use Group By:
SELECT Status, Count(Status)
FROM table
GROUP BY Status
This will not return P = 0 if P is not populated in the table. In your application logic you will need to check and if a certain status is not returned, it means there are no entries (i.e. 0).
SQL cannot query records that are not there.
This will return a row for every status and the count in the second column:
SELECT Status, COUNT(*) Cnt
FROM Tbl
GROUP BY Status
So it would return
Status Cnt
U 3
T 1
L 1
for your example (in no defined order). Use ORDER BY if you want to sort the results.
You can do this with a query which groups on your status column, e.g.
SELECT COUNT(*) as StatusCount, Status
FROM MyTable
GROUP BY Status
To get the zero for the status P, you have to do some devious stuff using a table that lists all the possible statuses.
SELECT COUNT(A.Status), B.Status
FROM AnonymousTable AS A RIGHT OUTER JOIN
(SELECT 'P' AS Status FROM Dual
UNION
SELECT 'U' AS Status FROM Dual
UNION
SELECT 'L' AS Status FROM Dual
UNION
SELECT 'T' AS Status FROM Dual
) AS B ON A.Status = B.Status
GROUP BY B.Status;
The 4-way UNION is one way of generating a list of values; your DBMS may provide more compact alternatives. I'm assuming that the table Dual contains just one row (as found in Oracle).
The COUNT(A.Status) counts the number of non-null values in A.Status. The RIGHT OUTER JOIN lists the row from B with Status = 'P' and joins it with a single NULL for the A.Status, which the COUNT(A.Status) therefore counts as zero. If you used COUNT(*), you'd get a 1 for the count.
Related
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');
sqlite returns row count while executing this query
SELECT sum(s.Card_10) ,sum(p.Card_10) FROM Sales_Table s , Purchase_Table p
ANSWER
is sum(s.Card_10) 4 and sum(card_10) is 40
but if any execute these queries separately it reruns correct answer
Select sum(Card_10) from sales_table
Answer
1
and
Select sum(Card_10) from Purchase_table
Answer
40
why the error happen in such type of JOINS ?
In the query
SELECT sum(s.Card_10) ,sum(p.Card_10) FROM Sales_Table s , Purchase_Table p
a cross join of sales_table and purchase_table would be performed. So if sales_table has 1 row with card_10 column value of 1 and purchase_table has 4 rows with different values of card_10 that sum up to 40.
So the cross join (with some dummy data) would look like
s.card_10 p.card_10
1 5
1 10
1 8
1 17
Hence you get the incorrect result.
One way of getting the correct counts in a single query is to use union.
select sum(Card_10) from sales_table
union all
select sum(Card_10) from Purachase_table
or
select max(fromsalestable) as fromsalestable, max(frompurchasetable) as frompurchasetable
from
(
select sum(Card_10) as fromsalestable, null as frompurchasetable from sales_table
union all
select null, sum(Card_10) from Purachase_table
) t
I have a table that records status on course progress. A new record is added for each user/course comination when a course is started. That record is updated with a 'completed' status when the course is completed. I need to find the records for users who have never completed any courses.
Example Table:
User Course Status
A 1 S
A 2 C
B 1 S
C 2 S
D 2 C
C 3 S
I need a query that finds the following:
User Course Status
B 1 S
C 2 S
C 3 S
Any help is appreciated.
select user, course, status
from your_table
where user in
(
select user
from your_table
group by user
having sum(CASE WHEN status = 'C' THEN 1 ELSE 0 END) = 0
)
Select User, Course, Status from MyTable where User not in (Select Distinct User from MyTable where Status = 'C')
SELECT User,Course,Status FROM YourTable a
LEFT JOIN
(SELECT DISTINCT User FROM YourTable WHERE Status='C') CompletedAnything
ON a.User=CompletedAnything.User
WHERE COmpletedAnything.User IS NULL
Here's a SQL Fiddle that gives you what you want:
http://sqlfiddle.com/#!2/b6988/1
Query is this:
select User, Course, Status
from mytable
where User not in
(select distinct User from mytable where status = 'C' ans User is not null)
database table like this
============================
= suburb_id | value
= 1 | 2
= 1 | 3
= 2 | 4
= 3 | 5
query is
SELECT COUNT(suburb_id) AS total, suburb_id
FROM suburbs
where suburb_id IN (1,2,3,4)
GROUP BY suburb_id
however, while I run this query, it doesn't give COUNT(suburb_id) = 0 when suburb_id = 0
because in suburbs table, there is no suburb_id 4, I want this query to return 0 for suburb_id = 4, like
============================
= total | suburb_id
= 2 | 1
= 1 | 2
= 1 | 3
= 0 | 4
A GROUP BY needs rows to work with, so if you have no rows for a certain category, you are not going to get the count. Think of the where clause as limiting down the source rows before they are grouped together. The where clause is not providing a list of categories to group by.
What you could do is write a query to select the categories (suburbs) then do the count in a subquery. (I'm not sure what MySQL's support for this is like)
Something like:
SELECT
s.suburb_id,
(select count(*) from suburb_data d where d.suburb_id = s.suburb_id) as total
FROM
suburb_table s
WHERE
s.suburb_id in (1,2,3,4)
(MSSQL, apologies)
This:
SELECT id, COUNT(suburb_id)
FROM (
SELECT 1 AS id
UNION ALL
SELECT 2 AS id
UNION ALL
SELECT 3 AS id
UNION ALL
SELECT 4 AS id
) ids
LEFT JOIN
suburbs s
ON s.suburb_id = ids.id
GROUP BY
id
or this:
SELECT id,
(
SELECT COUNT(*)
FROM suburb
WHERE suburb_id = id
)
FROM (
SELECT 1 AS id
UNION ALL
SELECT 2 AS id
UNION ALL
SELECT 3 AS id
UNION ALL
SELECT 4 AS id
) ids
This article compares performance of the two approaches:
Aggregates: subqueries vs. GROUP BY
, though it does not matter much in your case, as you are querying only 4 records.
Query:
select case
when total is null then 0
else total
end as total_with_zeroes,
suburb_id
from (SELECT COUNT(suburb_id) AS total, suburb_id
FROM suburbs
where suburb_id IN (1,2,3,4)
GROUP BY suburb_id) as dt
#geofftnz's solution works great if all conditions are simple like in this case. But I just had to solve a similar problem to generate a report where each column in the report is a different query. When you need to combine results from several select statements, then something like this might work.
You may have to programmatically create this query. Using left joins allows the query to return rows even if there are no matches to suburb_id with a given id. If your db supports it (which most do), you can use IFNULL to replace null with 0:
select IFNULL(a.count,0), IFNULL(b.count,0), IFNULL(c.count,0), IFNULL(d.count,0)
from (select count(suburb_id) as count from suburbs where id=1 group by suburb_id) a,
left join (select count(suburb_id) as count from suburbs where id=2 group by suburb_id) b on a.suburb_id=b.suburb_id
left join (select count(suburb_id) as count from suburbs where id=3 group by suburb_id) c on a.suburb_id=c.suburb_id
left join (select count(suburb_id) as count from suburbs where id=4 group by suburb_id) d on a.suburb_id=d.suburb_id;
The nice thing about this is that (if needed) each "left join" can use slightly different (possibly fairly complex) query.
Disclaimer: for large data sets, this type of query might have not perform very well (I don't write enough sql to know without investigating further), but at least it should give useful results ;-)
I have a table in my database:
Name | Element
1 2
1 3
4 2
4 3
4 5
I need to make a query that for a number of arguments will select the value of Name that has on the right side these and only these values.
E.g.:
arguments are 2 and 3, the query should return only 1 and not 4 (because 4 also has 5). For arguments 2,3,5 it should return 4.
My query looks like this:
SELECT name FROM aggregations WHERE (element=2 and name in (select name from aggregations where element=3))
What do i have to add to this query to make it not return 4?
A simple way to do it:
SELECT name
FROM aggregations
WHERE element IN (2,3)
GROUP BY name
HAVING COUNT(element) = 2
If you want to add more, you'll need to change both the IN (2,3) part and the HAVING part:
SELECT name
FROM aggregations
WHERE element IN (2,3,5)
GROUP BY name
HAVING COUNT(element) = 3
A more robust way would be to check for everything that isn't not in your set:
SELECT name
FROM aggregations
WHERE NOT EXISTS (
SELECT DISTINCT a.element
FROM aggregations a
WHERE a.element NOT IN (2,3,5)
AND a.name = aggregations.name
)
GROUP BY name
HAVING COUNT(element) = 3
It's not very efficient, though.
Create a temporary table, fill it with your values and query like this:
SELECT name
FROM (
SELECT DISTINCT name
FROM aggregations
) n
WHERE NOT EXISTS
(
SELECT 1
FROM (
SELECT element
FROM aggregations aii
WHERE aii.name = n.name
) ai
FULL OUTER JOIN
temptable tt
ON tt.element = ai.element
WHERE ai.element IS NULL OR tt.element IS NULL
)
This is more efficient than using COUNT(*), since it will stop checking a name as soon as it finds the first row that doesn't have a match (either in aggregations or in temptable)
This isn't tested, but usually I would do this with a query in my where clause for a small amount of data. Note that this is not efficient for large record counts.
SELECT ag1.Name FROM aggregations ag1
WHERE ag1.Element IN (2,3)
AND 0 = (select COUNT(ag2.Name)
FROM aggregatsions ag2
WHERE ag1.Name = ag2.Name
AND ag2.Element NOT IN (2,3)
)
GROUP BY ag1.name;
This says "Give me all of the names that have the elements I want, but have no records with elements I don't want"