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

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

Related

Select ID with specific values in more than one field

I have a table as follows
groupCode
ProductIdentifier
1
dental
1
membership
2
dental
2
vision
2
health
3
dental
3
vision
I need to find out if a specific groupCode have "dental", "vision" and "health" (all three simultaneously)
The expected result is code 2
What I need to identify is if groupCode 2 has the three products (or two, or whatever the user enters). This is part of a huge kitchen sink query I'm building.
I'm doing
SELECT groupCode
FROM dbo.table
WHERE (productIdentifier = N'dental')
AND (productIdentifier = N'vision')
AND (productIdentifier = N'health')
AND (groupCode = 2)
But clearly is wrong because it's not working.
I tried to do something like its described here but it didn't return a result for me:
Select rows with same id but different value in another column
Thanks.
If each of 'dental','vision' and 'health' occur only once per group identifier, you can group by group identifier and filter by the groups having count(*) = 3:
WITH
-- your input ..
indata(groupCode,ProductIdentifier) AS (
SELECT 1,'dental'
UNION ALL SELECT 1,'membership'
UNION ALL SELECT 2,'dental'
UNION ALL SELECT 2,'vision'
UNION ALL SELECT 2,'health'
UNION ALL SELECT 3,'dental'
UNION ALL SELECT 3,'vision'
)
-- real query starts here ...
SELECT
groupcode
FROM indata
WHERE productidentifier IN ('dental','vision','health')
GROUP BY
groupcode
HAVING COUNT(*) = 3;
-- out groupcode
-- out -----------
-- out 2
As per Marcothesane answer, if you know the groupCode (2) and the number of products (vision, dental and health), 3 in this case, and you need to confirm if that code has those three specific products, this will work for you:
SELECT COUNT(groupCode) AS totalRecords
FROM dbo.table
WHERE (groupCode = 2) AND (productIdentifier IN ('dental', 'vision', 'health'))
HAVING (COUNT(groupCode) = 3)
This will return 3 (number of records = number of products).
Its basically's Marcothesane answer in a way you can "copy/paste" to your code by just changing the table name. You should accept Marcothesane answer.

UNION table in second query based on the result of first query in Oracle sql

I am struggling with following problem (in further I provide pseudocode in order to make my example more understandable)
Assume I have 2 queries which result I want to union with each other
What query1 output looks like:
ID OFFER
1 prod_1
2 prod_2
3 prod_2
4 prod_1
What query2 output looks like:
ID SEGMENT
1 LOW
2 HIGH
3 MED
999 MED
What I need to do is to union results of this 2 queries, but avoid taking row with ID = 999
If there any way to do it using UNION by extracting from query2 rows bases on values of column ID which are present in column ID of query2?
I know that following code is incorrect but it conveys the idea of ​​the question greatly:
--query1
(
SELECT ID, OFFER
FROM TAB1
WHERE ID <= 4
) RES1
UNION
--query2
SELECT ID, SEGMENT
FROM TAB1
WHERE ID IN (SELECT ID FROM RES2)
Result should be as following
ID OFFER
1 prod_1
2 prod_2
3 prod_2
4 prod_1
1 LOW
2 HIGH
3 MED
Appreciate your help
Your pseudo code comes very close. You can use WITH for convenience:
WITH q1 AS (SELECT id, offer FROM tab1 WHERE id <= 4)
, q2 AS (SELECT id, segment FROM tab1 WHERE id IN (SELECT id FROM q1))
SELECT * FROM q1
UNION ALL
SELECT * FROM q2;
(Be aware though that you can get the rows in any order, if you don't specify an ORDER BY clause.)

SQL Query where a specific column should have two specific values but have just one

Sorry for the cryptic title, but i didn't find a better question
I have a Table with lets say these Data:
Table Article
ID ArticleNumber Type
1 10 1
2 10 3
3 20 1
4 30 1
5 30 3
I'm looking for the 3. Row where no type 3 article exists but a type 1 article exists.
I think it have to be a very easy SQL query but i can't find a solution...
To get all ArticleNumbers for which exists a row with type = 1 but there is no row with type = 3, use this:
SELECT
*
FROM
Article a
WHERE
a.`Type` = 1
AND NOT EXISTS (
SELECT * FROM
Article a2
WHERE
a2.`Type` = 3
AND a2.ArticleNumber = a.ArticleNumber
)
You can select the first ID by aggregating the original data (see subquery), then you can filter those records where could be any anomalies.
If you want, you can change the aggregation to COUNT, then you can compare the count for each types.
SELECT
ArticleNumber
FROM (
-- Conditional aggregation to find the first ID for each type per ArticleNumber
SELECT
ArticleNumber
, MIN(CASE WHEN Type = 1 THEN ID ELSE NULL END) AS Type_1
, MIN(CASE WHEN Type = 3 THEN ID ELSE NULL END) AS Type_3
GROUP BY
ArticleNumber
) AS X
WHERE
Type_1 IS NULL
OR Type_2 IS NULL
Note You can change the above query to use the HAVING keyword instead of subquery if you want.
Another variant, using MINUS to remove the articles with Type 3:
select ID, ArticleNumber from Article where type = 1
MINUS
select ID, ArticleNumber from Article where type = 3
UPDATE
After re-reading your question, I guess you want to also have those with Type = 3, but where no row with Type = 1 exists. For this, you can use COUNT DISTINCT:
select * from (
select ID, ArticleNumber, count(distinct type) as type_cnt
from Article
group by ID, ArticleNumber
)
where type_cnt < 2
You mean you want all rows where type is not 3?
That would be this statement:
SELECT
*
FROM
`Article`
WHERE
`type` != 3;
After re-reading your question I understand your problem. I would go with Eriks solution.

Returning several rows from a single query, based on a value of a column

Let's say I have this table:
|Fld | Number|
1 5
2 2
And I want to make a select that retrieves as many Fld as the Number field has:
|Fld |
1
1
1
1
1
2
2
How can I achieve this? I was thinking about making a temporary table and instert data based on the Number, but I was wondering if this could be done with a single Select statement.
PS: I'm new to SQL
You can join with a numbers table:
SELECT Fld
FROM yourtable
JOIN Numbers
ON yourtable.Number <= Numbers.Number
A numbers table is just a table with a list of numbers:
Number
1
2
3
etc...
Not an great solution (since you still query your table twice, but maybe you can work from it)
SELECT t1.fld, t1.number
FROM table t1, (
SELECT ROWNUM number FROM dual
CONNECT BY LEVEL <= (SELECT MAX(number) FROM t1)) t2
WHERE t2.number<=t1.number
It generates maximum amount of rows needed and then filters it by each row.
I don't know if your RDBMS version supports it (although I rather suspect it does), but here is a recursive version:
WITH remaining (fld, times) as (SELECT fld, 1
FROM <table>
UNION ALL
SELECT a.fld, a.times + 1
FROM remaining as a
JOIN <table> as b
ON b.fld = a.fld
AND b.number > a.times)
SELECT fld
FROM remaining
ORDER BY fld
Given your source data table, it outputs this (count included for verification):
fld times
=============
1 1
1 2
1 3
1 4
1 5
2 1
2 2

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

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.