mysql SELECT COUNT(*) ... GROUP BY ... not returning rows where the count is zero - sql

SELECT student_id, section, count( * ) as total
FROM raw_data r
WHERE response = 1
GROUP BY student_id, section
There are 4 sections on the test, each with a different number of questions. I want to know, for each student, and each section, how many questions they answered correctly (response=1).
However, with this query, if a student gets no questions right in a given section, that row will be completely missing from my result set. How can I make sure that for every student, 4 rows are ALWAYS returned, even if the "total" for a row is 0?
Here's what my result set looks like:
student_id section total
1 DAP--29 3
1 MEA--16 2
1 NNR--13 1 --> missing the 4th section for student #1
2 DAP--29 1
2 MEA--16 4
2 NNR--13 2 --> missing the 4th section for student #2
3 DAP--29 2
3 MEA--16 3
3 NNR--13 3 --> missing the 4th section for student #3
4 DAP--29 5
4 DAP--30 1
4 MEA--16 1
4 NNR--13 2 --> here, all 4 sections show up because student 4 got at least one question right in each section
Thanks for any insight!
UPDATE: I tried
SELECT student_id, section, if(count( * ) is null, 0, count( * )) as total
and that didn't change the results at all. Other ideas?
UPDATE 2: I got it working thanks to the response below:
SELECT student_id, section, SUM(CASE WHEN response = '1' THEN 1 ELSE 0 END ) AS total
FROM raw_data r
WHERE response = 1
GROUP BY student_id, section

SELECT student_id, section, sum(case when response=1 then 1 else 0 end) as total
FROM raw_data_r GROUP BY student_id, section
Note that there's no WHERE condition.

SELECT r.student_id,
r.subject,
sum( r.response ) as total
FROM raw_data r
GROUP BY student_id, subject

if you have a separate table with student information, you can select students from that table and left join the results to the data_raw table:
SELECT si.student_name, rd.student_id, rd.section, rd.count(*) AS total
FROM student_info AS si LEFT JOIN raw_data AS rd USING rd.student_id = si.student_id
This way, it first selects all students, then executes the count command.

Related

How to update table by of the records in the table

I have a table in PostgerSQL and I need to make N entries in the table twice and for the first half I need to fill in the partner_id field with the value 1 and the second half with the value partner_id = 2.
i try to `
update USERS_TABLE set user_rule_id = 1;
update USERS_TABLE set user_rule_id = 2 where USERS_TABLE.id > count(*)/2;
`
I depends a lot how precise the number of users have to be that are updated with 1 or 2.
The following would be quite unprecise,a s it doesn't take the exact number of user that already exist8after deleting some rows the numbers doesn't fit anymore.
SELECT * FROM USERS_TABLE
id
user_rule_id
1
1
2
1
3
2
4
2
5
2
SELECT 5
If you have a lot of deleted rows and want still the half of the users, you can choose following approach, which does rely on the id, but at teh actual row number
UPDATE USERS_TABLE1
set user_rule_id = CASE WHEN rn <= (SELECT count(*) FROM USERS_TABLE1)/ 2 then 1
ELSE 2 END
FROM (SELECT id, ROW_NUMBER() OVER( ORDER BY id) rn FROM USERS_TABLE1) t
WHERE USERS_TABLE1.id = t.id;
UPDATE 5
SELECT * FROM USERS_TABLE1
id
user_rule_id
1
1
2
1
3
2
4
2
5
2
SELECT 5
fiddle
In the sample case it it the same result, but when you have a lot of rows and a bunch of the deleted users, the senind will give you quite a good result

SQL To delete number of items is less than required item number

I have two tables - StepModels (support plan) and FeedbackStepModels (feedback), StepModels keeps how many steps each support plan requires.
SELECT [SupportPlanID],COUNT(*)AS Steps
FROM [StepModels]
GROUP BY SupportPlanID
SupportPlanID (Steps)
-------------------------------
1 4
2 9
3 3
4 10
FeedbackStepModels keeps how many steps employee entered the system
SELECT [FeedbackID],SupportPlanID,Count(*)AS StepsNumber
FROM [FeedbackStepModels]
GROUP BY FeedbackID,SupportPlanID
FeedbackID SupportPlanID
---------------------------------------------
1 1 3 --> this suppose to be 4
2 2 9 --> Correct
3 3 0 --> this suppose to be 3
4 4 10 --> Correct
If submitted Feedback steps total is less then required total amount I want to delete this wrong entry from the database. Basically i need to delete FeedbackID 1 and 3.
I can load the data into List and compare and delete it, but want to know if we can we do this in SQL rather than C# code.
You can use the query below to remove your unwanted data by SQL Script
DELETE f
FROM FeedbackStepModels f
INNER JOIN (
SELECT [FeedbackID],SupportPlanID, Count(*) AS StepsNumber
FROM [FeedbackStepModels]
GROUP BY FeedbackID,SupportPlanID
) f_derived on f_derived_FeedbackID=f.FeedBackID and f_derived.SupportPlanID = f.SupportPlanID
INNER JOIN (
SELECT [SupportPlanID],COUNT(*)AS Steps
FROM [StepModels]
GROUP BY SupportPlanID
) s_derived on s_derived.SupportPlanID = f.SupportPlanID
WHERE f_derived.StepsNumber < s_derived.Steps
I think you want something like this.
DELETE FROM [FeedbackStepModels]
WHERE FeedbackID IN
(
SELECT a.FeedbackID
FROM
(
SELECT [FeedbackID],
SupportPlanID,
COUNT(*) AS StepsNumber
FROM [FeedbackStepModels]
GROUP BY FeedbackID,
SupportPlanID
) AS a
INNER JOIN
(
SELECT [SupportPlanID],
COUNT(*) AS Steps
FROM [StepModels]
GROUP BY SupportPlanID
) AS b ON a.SupportPlanID = b.[SupportPlanID]
WHERE a.StepsNumber < b.Steps
);

Top 1 & Top 1 1 ( Expr1000 1 ) typical output

SELECT TOP 1 * FROM Customers;
This gives me all customer details of first row
( customerid, customer name etc..,)
SELECT TOP 1 1 FROM Customers;
I got o/p as
Expr1000
------------
1
But I genuinely don't understand above output, and I searched for it but couldn't get the clear understanding of it.
If your first query * mean show all fields
But:
SELECT TOP 1 1 FROM Customers;
is equal to:
SELECT TOP 1
1 as myConstantField FROM Customers;
So you create a single constant field and you will get 1 for every row in Customers, but TOP 1 will filter to only the first one
The second "1" is not considered as a column, it's just a constant. So with this query, you ask to write down one (TOP 1) row with one column with the constant "1". Same thing will happen if you write something like this :
SELECT TOP 1 'Hey you', 1 as valueone ,2 ,3, customerid
FROM Customers ;
You'll have one row with the values 'Hey you' , '1' in column "valueone", '2', '3' then your customerid from the first row
When you
SELECT 1
it will show just one row of 1. When you
SELECT 1 FROM Customers
it will give you the number of rows that there in Customers. So when you
SELECT TOP 1 1 FROM Customers
it will give you only the first row of 1 as if you did
SELECT TOP 1 * FROM Customers

SQL Find the number of orders in each line count

I have a SQL question from one of the well known IT company couple month ago when they were interviewing me, and I never got it figured out.
An order can have multiple lines - For ex., if a customer ordered cookies,
chocolates, and bread, this would count as 3 lines in one order. The question
is to find the number of orders in each line count. The output of this query
would be something like 100 orders had 1 line, 70 orders had 2 lines, 30 had 3
lines, and so on. This table has two columns - order_id and line_id
Sample Data:
order_id line_id
1 cookies
1 chocolates
1 bread
2 cookies
2 bread
3 chocolates
3 cookies
4 milk
desired output:
orders line
1 1
2 2
1 3
So generally speaking, we have a very large data set, and the line_id per order_id can be ranging from 1 to infinite(Theoretically speaking).
The desired output for the general case is:
orders line
100 1
70 2
30 3
etc..
How can I write a query to find the total number of orders per line count=1,2,3... etc
My thought on this problem is to first subquery the count of line_id per order_id.
And then select the subquery along with a list of values as the second column ranging from 1 to max(lines_id per order)
Test Data:
create table data
(
order_id int,
line_id char(50)
);
insert into data
values(1, 'cookies'),
(1, 'chocolates'),
(1, 'bread'),
(2, 'bread'),
(2, 'cookies'),
(3, 'chocolates'),
(3, 'cookies'),
(4, 'milk');
Since order_id=1 has 3 lines,
order_id=2 has 2 lines,
order_id=3 has 2 lines,
order_id=4 has 1 line.
Thus it yield our solution:
orders line
1 1
2 2
1 3
This is because both order_id = 2 and 3 has 2 lines. So it would mean 2 orders has line = 2.
So far, I have:
select lines,
sum(case when orders_per_line = '1' then 1 else 0),
sum(case when orders_per_line = '2' then 1 else 0),
sum(case when orders_per_line = '3' then 1 else 0)
from(
select lines, order_id, count(*) as orders_per_line from data
where lines in ('1, '2', '3')
group by order_id, lines
)
group by lines
My query is wrong, as I only want 2 columns, and also creating a sequence of numbers ranging from 1 to max(lines per order) is also wrong.
Any suggestions?
Thanks in advance!
Try this:
Select Count(*) as Orders, Lines from (
Select order_id, Count(*) as Lines from data group by order_id
)query group by Lines
For exmaple, look at this sqlfiddle
Try This:
with a AS
(
SELECT
COUNT(order_id) AS Orders
FROM
Table_1
GROUP BY
Order_Id
)
SELECT
Orders,
COUNT(*) AS line
FROM
a
GROUP BY Orders
Basically, it just count how many times the order_id are repeated:
SELECT order_id, count(order_id) FROM data GROUP BY order_id

Exclude value of a record in a group if another is present v2

In the example table below, I'm trying to figure out a way to sum amount over marks in two situations: the first, when mark 'C' exists within a single id, and the second, when mark 'C' doesn't exist within an id (see id 1 or 2). In the first situation, I want to exclude the amount against mark 'A' within that id (see id 3 in the desired conversion table below). In the second situation, I want to perform no exclusion and take a simple sum of the amounts against the marks.
In other words, for id's containing both mark 'A' and 'C', I want to make the amount against 'A' as zero. For id's that do not contain mark 'C' but contain mark 'A', keep the original amount against mark 'A'.
My desired output is at the bottom. I've considered trying to partition over id or use the EXISTS command, but I'm having trouble conceptualizing the solution. If any of you could take a look and point me in the right direction, it would be greatly appreciated :)
example table:
id mark amount
------------------
1 A 1
2 A 3
2 B 2
3 A 1
3 C 3
desired conversion:
id mark amount
------------------
1 A 1
2 A 3
2 B 2
3 A 0
3 C 3
desired output:
mark sum(amount)
--------------------
A 4
B 2
C 3
You could slightly modify my previous answer and end up with this:
SELECT
mark,
sum(amount) AS sum_amount
FROM atable t
WHERE mark <> 'A'
OR NOT EXISTS (
SELECT *
FROM atable
WHERE id = t.id
AND mark = 'C'
)
GROUP BY
mark
;
There's a live demo at SQL Fiddle.
Try:
select
mark,
sum(amount)
from ( select
id,
mark,
case
when (mark = 'A' and id in (select id from table where mark = 'C')) then 0
else amount
end as amount
from table ) t1
group by mark