I have a table A which has 85337 rows (Total).
Then the following query (Q1)
SELECT *
FROM A
WHERE 1 = 1
AND c1 = 0
AND c2 = 0
AND c3 = 0
AND c4 = 0;
returns 590 rows.
The next query (Q2):
SELECT *
FROM A
WHERE 1 = 1
AND c1 != 0
AND c2 != 0
AND c3 != 0
AND c4 != 0;
returns: 44245 rows. (should return 84747)
Why (Total) does not equal sum of (Q1) + (Q2)?
Why in the second query has to be "OR" instead of "AND" to get the "correct" values when the logic is the same? It must be trivial but cannot imagine the logic behind.
Based on De Morgan's laws on Negation:
NOT (P AND Q) => (NOT P OR NOT Q)
This is because your two queries don't encapsulate every possible combination of your data as you are using AND:
declare #a table (c1 int,c2 int,c3 int,c4 int);
insert into #a values
(1,1,1,1)
,(0,0,1,1) -- This row is not returned as it doesn't meet either criteria below
,(0,0,0,0)
,(0,0,0,0);
SELECT *
FROM #a
WHERE 1 = 1
AND c1 = 0
AND c2 = 0
AND c3 = 0
AND c4 = 0;
SELECT *
FROM #a
WHERE 1 = 1
AND c1 != 0
AND c2 != 0
AND c3 != 0
AND c4 != 0;
Related
How can I find if any value exists more than once in one row? An example:
id | c1 | c2 | c3
----+----+----+----
1 | a | b | c
2 | a | a | b
3 | b | b | b
The query should return rows 2 and 3 since they have the same value more than once. The solution I'm looking for is not 'where c1 = c2 or c1 = c3 or c2 = c3' since there can be any number of columns in tables I need to test. All values are text but can be any length.
One way to do that is to convert the columns to rows:
select *
from the_table tt
where exists (select 1
from ( values (c1), (c2), (c3) ) as t(v)
group by v
having count(*) > 1)
If you want a dynamic solution where you don't have to list each column, you can do that by converting the row to a JSON value:
select *
from the_table tt
where exists (select 1
from jsonb_each_text(to_jsonb(tt)) as j(k,v)
group by v
having count(*) > 1)
Online example
SELECT c.period,
c.idsr_incident_id_id,
c.idsr_disease_id_id,
CASE WHEN idsr_incident_id_id = 1 OR idsr_incident_id_id = 3 THEN SUM(c.data_value::integer) ELSE 0 END AS cases,
CASE WHEN idsr_incident_id_id = 2 OR idsr_incident_id_id = 4 THEN SUM(c.data_value::integer) ELSE 0 END AS deaths
FROM veoc_idsr_weekly_national_report c
LEFT JOIN veoc_idsr_diseases b ON b.id = c.idsr_disease_id_id
LEFT JOIN veoc_idsr_reported_incidents j ON j.id = c.idsr_incident_id_id
WHERE c.idsr_incident_id_id >= 1 AND c.idsr_incident_id_id <= 4 AND idsr_disease_id_id = 10 AND period ='2019W30'
GROUP BY c.period, c.idsr_incident_id_id, c.idsr_disease_id_id, c.data_value;`enter code here`
Below my query results:
I want to sum the cases and the death value columns since the period and disease_id is the same. I think my problem is on the Group By section but I cant solve it. If it means i must remove the incident_id column to get the totals it will still be fine.
Please, try with below query where sum of function place replaced:
SELECT c.period,
c.idsr_disease_id_id,
SUM(CASE WHEN idsr_incident_id_id = 1 OR idsr_incident_id_id = 3 THEN (c.data_value::integer) ELSE 0 END) AS cases,
SUM(CASE WHEN idsr_incident_id_id = 2 OR idsr_incident_id_id = 4 THEN (c.data_value::integer) ELSE 0 END) AS deaths
FROM veoc_idsr_weekly_national_report c
LEFT JOIN veoc_idsr_diseases b ON b.id = c.idsr_disease_id_id
LEFT JOIN veoc_idsr_reported_incidents j ON j.id = c.idsr_incident_id_id
WHERE c.idsr_incident_id_id >= 1 AND c.idsr_incident_id_id <= 4 AND idsr_disease_id_id = 10 AND period ='2019W30'
GROUP BY c.period, c.idsr_disease_id_id;
I have a simple but very important concept to clear in T-SQL.
I am writing a lot of T-SQL queries against a table, with a lot of aggregations and GROUP BY.
Now, in the SELECT clause of my T-SQL query, I have a CASE-WHEN statements. Please see below:
Statement 1:
SELECT X, Y, Z,
A = CASE
WHEN P = 1 THEN B
ELSE Q
END,
SUM(Sales)
FROM mytable
GROUP BY
X, Y, Z,
CASE
WHEN P = 1 THEN B
ELSE Q
END
Now can Statement 1 be written as Statement 2 ?
Statement 2:
SELECT X, Y, Z,
A = CASE
WHEN P = 1 THEN B
ELSE Q
END,
SUM(Sales)
FROM mytable
GROUP BY
X, Y, Z,
P, B, Q
Is Statement 1 = Statement 2 ?
Can the CASE-WHEN in the SELECT clause be modified in the GROUP BY clause into individual columns?
Will the result set be the same always ?
The difference relies on the amount of different values you might get from columns P, B and Q, against the result of your CASE statement. You can spot the different on this example.
IF OBJECT_ID('tempdb..#Data') IS NOT NULL
DROP TABLE #Data
CREATE TABLE #Data (
P INT,
B INT,
Q INT,
Sales INT)
INSERT INTO #Data (
P,
B,
Q,
Sales)
VALUES
(1, 20, 300, 1000),
(1, 20, 400, 500),
(2, 1, 1, 50),
(2, 1, 1, 250)
-- Statement 2
SELECT
P,
B,
Q,
TotalSales = SUM(D.Sales)
FROM
#Data AS D
GROUP BY
P,
B,
Q
/*
All different combinations of PBQ and listed, and their sales added
P B Q TotalSales
1 20 300 1000
1 20 400 500
2 1 1 300
*/
-- Statement 1
SELECT
CaseResult = CASE WHEN P = 1 THEN B ELSE Q END,
TotalSales = SUM(D.Sales)
FROM
#Data AS D
GROUP BY
CASE WHEN P = 1 THEN B ELSE Q END
/*
The grouping value depends on value B when P = 1 (and not on Q!) so
all records with P = 1 and same B are grouped together and
all records with P = 0 and same Q are grouped together
CaseResult TotalSales
1 300
20 1500
*/
There might be the case when you data doesn't generate different values from the CASE to the combination of P, B and Q, in that case the results will be the same for both queries.
I've a table having data as:
C1 || C2
-----------------
a || 1
b || 1
c || 1
a || 0
b || 0
c || 0
d || 0
I've to delete row 4,5,6. All rows with C2 = 0, that have same C1 and C2 = 1. Suggestions ?
delete from your_table
where c2 = 0
and c1 in
(
select c1
from your_table
where c2 in (0,1)
group by c1
having count(distinct c2) = 2
)
I have a table containing five boolean columns.
How can I construct a query that returns rows where at least 2 columns are true?
Cast the boolean types to integer (0=false, 1=true) and check their sum:
select *
from my_table
where a::int + b::int + c::int + d::int + e::int >= 2;
The long way:
SELECT * from t where c1 and c2 or c1 and c3 or c1 and c4 or c1 and c5
or c2 and c3 or c2 and c4 or c2 and c5 or c3 and c4 or c3 and c5 or c4 and c5;
The accepted answer only works assuming all columns are defined NOT NULL, which has not been specified. To make it work with NULL, too:
SELECT *
FROM tbl
WHERE (a IS TRUE)::int
+ (b IS TRUE)::int
+ (c IS TRUE)::int
+ (d IS TRUE)::int
+ (e IS TRUE)::int > 1;
Or:
SELECT *
FROM tbl
WHERE COALESCE(a::int, 0)
+ COALESCE(b::int, 0)
+ COALESCE(c::int, 0)
+ COALESCE(d::int, 0)
+ COALESCE(e::int, 0) > 1;