COUNT() columns by a specific value - sql

I want to make a query on a SQL Compact 4.0 DB-Table, with 2 COUNT()-columns. The first column shall count all rows ( COUNT(*) ) and the second one shall only count the row, when the decimal-value of a specific column is higher as or equal to 3.0
I got this far:
SELECT COUNT(a.number) AS Participant, COUNT(b.specificColumn) AS Approved
FROM person AS a
LEFT OUTER JOIN test AS b
ON b.number = a.number
This way the second COUNT() will obviously only count rows, that actually have a value != NULL

I don't think you can do it using a count. Try using a case statement. Not tested:
SELECT COUNT(a.number) AS Participant,
SUM(case when b.specificColumn >3 then 1 else 0 end) as Approved
FROM person AS a
LEFT OUTER JOIN test AS b
ON b.number = a.number

SELECT COUNT(a.number) AS Participant,
SUM(CASE WHEN b.specificColumn IS NULL THEN 0
WHEN b.specificColumn >= 3 THEN 1
ELSE 0) AS Approved
FROM person AS a
LEFT OUTER JOIN test AS b
ON b.number = a.number

Related

SQL: How to remove duplicate rows created by CASE WHEN statement

I have two tables: (A) customers of the gym and (B) customers of the restaurant. I want to create an indicator in table (A) to indicate the customers who have been to both the gym and the restaurant on the same day. In accomplishing this, I used the following SQL script, but it created duplicate rows:
SELECT *,
CASE WHEN a.GymDate = b.RestaurantDate THEN 'Meal + Gym on the same day'
ELSE 'Gym Only' END AS 'Meal+Gym'
FROM Table_A a
LEFT JOIN Table_B b
ON a.customerid = b.customerid;
May I know how to keep only Table_A, but with the addition of the 'Meal+Gym' Indicator? Thanks!
A case expression does not generate rows, it is your join that is generating the duplicate rows. You could add the date predicate to the join condition, and merely check for the existence of a record, e.g.
SELECT *,
CASE WHEN b.customerid IS NOT NULL THEN 'Meal + Gym on the same day'
ELSE 'Gym Only'
END AS [Meal+Gym]
FROM Table_A a
LEFT JOIN Table_B b
ON a.customerid = b.customerid
AND a.GymDate = b.RestaurantDate;
If table_B is not unique per customer/Date then you may need to do something like this to prevent duplicates:
SELECT *,
CASE WHEN r.RestaurantVisit IS NOT NULL THEN 'Meal + Gym on the same day'
ELSE 'Gym Only'
END AS [Meal+Gym]
FROM Table_A a
OUTER APPLY
( SELECT TOP 1 1
FROM Table_B b
WHERE a.customerid = b.customerid
AND a.GymDate = b.RestaurantDate
) AS r (RestaurantVisit);
N.B. While using single quotes works for column aliases, it is not a good habit at all, because it makes your column aliases indistinguishable from string literals other than from context. Even if this is clear to you, it probably isn't to other people, and since there's about a 10:1 ratio of reading:writing code, writing code that is easy to read is important. As such I've used square brackets for your column name instead
I would start with a table of customers, so you get an indicator for customers who have been to neither the gym nor a restaurant.
Then:
select c.*,
(case when exists (select 1
from table_a a join
table_b b
on a.customerid = b.customerid and
a.GymDate = b.RestaurantDate
where a.customerid = c.customerid
)
then 1 else 0
end) as same_day_gym_restaurant_flag
from customers c;
You can use CASE WHEN EXISTS instead of the LEFT JOIN:
SELECT *,
CASE WHEN EXISTS (
SELECT 1 FROM Table_B b
WHERE a.customerid = b.customerid
AND a.GymDate = b.RestaurantDate)
THEN 'Meal + Gym on the same day'
ELSE 'Gym Only'
END AS 'Meal+Gym'
FROM Table_A a
This assumes that you don't need any data from Table_B in the results.

Left join for 3 tables with Where not working as expected

I have 2 tables such as:
Versions Names Cell
ID Version ID Name Indicator ID Number
1 1.0 1 Joe Y 1 1234
2 1.1 1 Black N 2 5678
3 1.2 2 David N 3 4786
4 2.0 2 Troy N
2 George Y
Expected Output
ID Number Version Name Indicator
1 1234 1.0 Joe Y
2 5678 1.1 George Y
3 4786 1.2 NULL NULL
I want to get
all records from the "Versions" table with "Version" starting with "1" and
the "Name" and "Indicator" as "Y" from the "Names" table for records from #1 and
"Number" from the "Cell" table from #1
I think I need a LEFT JOIN. This does not give me the expected output. Where am I going wrong?
SELECT a.ID, b.Number, a.Version, c.Name, c.Indicator
FROM Version a LEFT JOIN Cell b ON a.ID = b.ID
LEFT JOIN Names c ON a.ID = c.ID
WHERE a.Version LIKE '1%' AND c.Indicator = 'Y'
Applying a condition on the "outer joined" table in the WHERE clause effectively turns the outer join into an inner join, because every row that is retained by the outer join will contain a null value in that column, but the condition c.Indicator = 'Y' in the where clause will remove those rows again.
To fix this, move c.Indicator = 'Y' into the join condition:
SELECT a.ID, b.Number, a.Version, c.Name, c.Indicator
FROM Version a
LEFT JOIN Cell b ON a.ID = b.ID
LEFT JOIN Names c ON a.ID = c.ID AND c.Indicator = 'Y'
WHERE a.Version LIKE '1%'
I'm answering for two reasons:
I think the version condition is not correct.
Fix the table aliases.
So, I think you really want:
SELECT v.ID, c.Number, v.Version, n.Name, n.Indicator
FROM Version v LEFT JOIN
Cell c
ON v.ID = c.ID LEFT JOIN
Names n
ON v.ID = n.ID AND n.Indicator = 'Y'
WHERE v.Version LIKE '1.%';
The reason for the . is so the WHERE clause does not match 11.0 or 101.3.
The reason for changing the table aliases is because the query with table abbreviations is much easier to follow.

Postgres - Left Join, same table

I have a table of subscription records for people subscribed to groups. There's a groupid column and a userid column (in addition to the primary key id). I want to find all the subscription rows of people subscribed to group 1 that do not also have a subscription record for group 2. I thought I could use an anti-join pattern, but I can't get it to work. I've tried:
SELECT
*
FROM subs s1
LEFT JOIN subs s2 ON s1.groupid=1 AND s2.groupid=2 AND s1.userid=s2.userid
WHERE s2.id IS NULL;
But that does not work (it returns subscription records with groupids that are not 1).
I would use group by for this:
select s.userid
from subs s
group by s.userid
having sum(case when s.groupid = 1 then 1 else 0 end) > 0 and
sum(case when s.groupid = 2 then 1 else 0 end) = 0;
Each condition in the having clause counts records for one of the groups. The first condition says there is at least one row with groupid = 1. The second says there are no records with groupid = 2.
You can do what you want with a left join as well. Here is the approach:
SELECT s1.*
FROM subs s1 LEFT JOIN
subs s2
ON s1.userid = s2.userid AND s2.groupid = 2
WHERE s1.groupid = 1 AND s2.id IS NULL;
Because it is a left join, the condition on the first table should go in the where clause.

Multiple COUNT in sql issue

I have a little wired issue.
I have to select two count from query Likes and Collects but when I add second query instead of 2 likes and 10 collects I get 10 likes and 10 collects.
What am I doing wrong here?
select COUNT(tl.ItemLikeId) as a, COUNT(tib.PacketId) as b
from Items i
left join ItemLikes il
on il.ItemId = i.ItemId
left join ItemsInPackets iip
on iip.ItemId = i.ItemId
where i.ItemId = 14591
Try SELECT COUNT(DISTINCT tl.ItemLikeId) AS a, COUNT(DISTINCT tib.PacketId) as b.
Your join gives you ten rows, so you have ten IDs from each table. However, not all of the IDs are unique. You're looking for unique IDs.
Count returns the number of rows. Not the number of rows with a value, and not the number of distinct rows.
To get number row rows with a value
select SUM(CASE WHEN tl.ItemLikeId IS NOT NULL THEN 1 ELSE 0 END) as a,
SUM(CASE WHEN tib.PacketId IS NOT NULL THEN 1 ELSE 0 END) as b
To get the number of distinct values, do what zimdanen suggested and use COUNT(DISTINCT)
select COUNT(DISTINCT tl.ItemLikeId) as a, COUNT(DISTINCT tib.PacketId) as b
Another approach, if all you are using ItemLikes and ItemsInPackets for are the counts
select
(
SELECT COUNT(ItemLikeId)
FROM ItemLikes
WHERE ItemId = i.ItemId
) as a,
(
SELECT COUNT(PacketId)
FROM ItemsInPackets
WHERE ItemId = i.ItemId
) as b
from Items i
where i.ItemId = 14591

SQL LEFT JOIN finding non-zero value as zero

I have the following query with many LEFT JOIN clauses that has 7 result columns, the last two of which are numbers. I'm expecting the count_non_zero column to always be equal to the count_total column (given the data I current have)
WITH temp_table AS (
SELECT
attr.company_name_id AS option_id,
attr.company_name AS option_name,
uj.internship_company_name_id,
AVG(CASE WHEN s.salary > 0 THEN s.salary END) AS average,
COUNT(CASE WHEN s.salary > 0 THEN attr.company_name END) as count_non_zero,
COUNT(attr.company_name_id) as count_total
FROM company_name attr
LEFT JOIN user_job_internship uj ON uj.internship_company_name_id = attr.company_name_id
AND attr.approved_by_administrator = 1
LEFT JOIN salary_internship s ON uj.user_job_internship_id = s.user_job_id
AND uj.job_type_id = 4
LEFT JOIN [user] u ON u.user_id = uj.user_id AND u.include_in_student_site_results = 1
AND u.site_instance_id IN (1)
LEFT JOIN user_education_mba_school mba ON u.user_id = mba.user_id
AND mba.mba_graduation_year_id NOT IN (8)
GROUP BY attr.company_name_id, attr.company_name, uj.internship_company_name_id)
SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY average DESC) AS row, *
FROM temp_table WHERE count_total >= 3) sub
WHERE row >= 1 AND row <= 25 ORDER BY average DESC;
I run this query to prove that no values in the 'salary' column are returning a value of 0.
SELECT s.* FROM user_job_internship uj, salary_internship s
where internship_company_name_id = 440
AND uj.user_job_internship_id = s.user_job_id
I'm thinking there is something that messes up the results that is causing the count_non_zero to get counts that do not exist. Anyone have anythoughts?
I am assuming your count_total is greater than your count_non_zero. That is to be expected because you are using outer join to join user_job_internship and salary_internship.
Your query is including companies that do not have any internships. A company will not be included in the count_non_zero if either the salary is 0 or if there is no internship at all.
Change those two joins to inner joins and you should get your expected result.
The other option is to change your count_total to ignore companies that haven't any internship
count(case when s.user_job_id is not null then attr.company_name_id end) as count_total
You have one other slight risk. Your count_non_zero is counting company_name whereas your count_total is counting company_name_id. You could have problems if the company_name column allows NULL values.