Select rows with same id but different result in another column - sql

sql: I have a table like this:
+------+------+
|ID |Result|
+------+------+
|1 |A |
+------+------+
|2 |A |
+------+------+
|3 |A |
+------+------+
|1 |B |
+------+------+
|2 |B |
+------+------+
The output should be something like:
Output:
+------+-------+-------+
|ID |Result1|Result2|
+------+-------+-------+
|1 |A |B |
+------+-------+-------+
|2 |A |B |
+------+-------+-------+
|3 |A | |
+------+-------+-------+
How can I do this?

SELECT
Id,
MAX((CASE result WHEN 'A' THEN 'A' ELSE NULL END)) result1,
MAX((CASE result WHEN 'B' THEN 'B' ELSE NULL END)) result2,
FROM
table1
GROUP BY Id
results
+------+-------+-------+
|Id |Result1|Result2|
+------+-------+-------+
|1 |A |B |
|2 |A |B |
|3 |A |NULL |
+------+-------+-------+
run live demo on SQL fiddle: (http://sqlfiddle.com/#!9/e1081/2)

there are a few ways to do it.
None of tehm a are straight forward.
in theory, a simple way would be to create 2 temporary tables, where you separte the data, all the "A" resultas in one table and "B" in another table.
Then get the results with simple query. using JOIN.
if you are allowed to use some scrpting on the process then it is simpler, other wise you need a more complex logic on your query. And for you query to alwasy work, you need to have some rules like, A table always contains more ids than B table.
If you post your real example, it is easier to get better answers.

for this reason:
ID Name filename
1001 swapan 4566.jpg
1002 swapan 678.jpg
1003 karim 7688.jpg
1004 tarek 7889.jpg
1005 karim fdhak.jpg
output:
ID Name filename
1001 swapan 4566.jpg 678.jpg
1003 karim 7688.jpg fdhak.jpg
1004 tarek 7889.jpg ...
.. ... ... ...

Related

In PostgreSQL, conditionally count rows

Background
I'm a novice Postgres user running a local server on a Windows 10 machine. I've got a dataset g that looks like this:
+--+---------+----------------+
|id|treatment|outcome_category|
+--+---------+----------------+
|a |1 |cardiovascular |
|a |0 |cardiovascular |
|b |0 |metabolic |
|b |0 |sensory |
|c |1 |NULL |
|c |0 |cardiovascular |
|c |1 |sensory |
|d |1 |NULL |
|d |0 |cns |
+--+---------+----------------+
The Problem
I'd like to get a count of outcome_category by outcome_category for those id who are "ever treated" -- defined as "id's who have any row where treatment=1".
Here's the desired result:
+----------------+---------+
|outcome_category| count |
+----------------+---------+
|cardiovascular | 3 |
|sensory | 1 |
|cns | 1 |
+----------------+---------+
It would be fine if the result had to contain metabolic, like so:
+----------------+---------+
|outcome_category|treatment|
+----------------+---------+
|cardiovascular | 3 |
|metabolic | 0 |
|sensory | 1 |
|cns | 1 |
+----------------+---------+
Obviously I don't need the rows to be in any particular order, though descending would be nice.
What I've tried
Here's a query I've written:
select treatment, outcome_category, sum(outcome_ct)
from (select max(treatment) as treatment,
outcome_category,
count(outcome_category) as outcome_ct
from g
group by outcome_category) as sub
group by outcome_category, sub.treatment;
But it's a mishmash result:
+---------+----------------+---+
|treatment|outcome_category|sum|
+---------+----------------+---+
|1 |cardiovascular |3 |
|1 |sensory |2 |
|0 |metabolic |1 |
|1 |NULL |0 |
|0 |cns |1 |
+---------+----------------+---+
I'm trying to identify the "ever exposed" id's using that first line in the subquery: select max(treatment) as treatment. But I'm not quite getting at the rest of it.
EDIT
I realized that the toy dataset g I originally gave you above doesn't correspond to the idiosyncrasies of my real dataset. I've updated g to reflect that many id's who are "ever treated" won't have a non-null outcome_category next to a row with treatment=1.
Interesting little problem. You can do:
select
outcome_category,
count(x.id) as count
from g
left join (
select distinct id from g where treatment = 1
) x on x.id = g.id
where outcome_category is not null
group by outcome_category
order by count desc
Result:
outcome_category count
----------------- -----
cardiovascular 3
sensory 1
cns 1
metabolic 0
See running example at db<>fiddle.
This would appear to be just a simple aggregation,
select outcome_category, Count(*) count
from t
where treatment=1
group by outcome_category
order by Count(*) desc
Demo fiddle

Conditional count of rows where at least one peer qualifies

Background
I'm a novice SQL user. Using PostgreSQL 13 on Windows 10 locally, I have a table t:
+--+---------+-------+
|id|treatment|outcome|
+--+---------+-------+
|a |1 |0 |
|a |1 |1 |
|b |0 |1 |
|c |1 |0 |
|c |0 |1 |
|c |1 |1 |
+--+---------+-------+
The Problem
I didn't explain myself well initially, so I've rewritten the goal.
Desired result:
+-----------------------+-----+
|ever treated |count|
+-----------------------+-----+
|0 |1 |
|1 |3 |
+-----------------------+-----+
First, identify id that have ever been treated. Being "ever treated" means having any row with treatment = 1.
Second, count rows with outcome = 1 for each of those two groups. From my original table, the ids who are "ever treated" have a total of 3 outcome = 1, and the "never treated", so to speak, have 1 `outcome = 1.
What I've tried
I can get much of the way there, I think, with something like this:
select treatment, count(outcome)
from t
group by treatment;
But that only gets me this result:
+---------+-----+
|treatment|count|
+---------+-----+
|0 |2 |
|1 |4 |
+---------+-----+
For the updated question:
SELECT ever_treated, sum(outcome_ct) AS count
FROM (
SELECT id
, max(treatment) AS ever_treated
, count(*) FILTER (WHERE outcome = 1) AS outcome_ct
FROM t
GROUP BY 1
) sub
GROUP BY 1;
ever_treated | count
--------------+-------
0 | 1
1 | 3
db<>fiddle here
Read:
For those who got no treatment at all (all treatment = 0), we see 1 x outcome = 1.
For those who got any treatment (at least one treatment = 1), we see 3 x outcome = 1.
Would be simpler and faster with proper boolean values instead of integer.
(Answer to updated question)
here is an easy to follow subquery logic that works with integer:
select subq.ever_treated, sum(subq.count) as count
from (select id, max(treatment) as ever_treated, count(*) as count
from t where outcome = 1
group by id) as subq
group by subq.ever_treated;

In SQL, query a table by transposing column results

Background
Forgive the title of this question, as I'm not really sure how to describe what I'm trying to do.
I have a SQL table, d, that looks like this:
+--+---+------------+------------+
|id|sex|event_type_1|event_type_2|
+--+---+------------+------------+
|a |m |1 |1 |
|b |f |0 |1 |
|c |f |1 |0 |
|d |m |0 |1 |
+--+---+------------+------------+
The Problem
I'm trying to write a query that yields the following summary of counts of event_type_1 and event_type_2 cut (grouped?) by sex:
+-------------+-----+-----+
| | m | f |
+-------------+-----+-----+
|event_type_1 | 1 | 1 |
+-------------+-----+-----+
|event_type_2 | 2 | 1 |
+-------------+-----+-----+
The thing is, this seems to involve some kind of transposition of the 2 event_type columns into rows of the query result that I'm not familiar with as a novice SQL user.
What I've tried
I've so far come up with the following query:
SELECT event_type_1, event_type_2, count(sex)
FROM d
group by event_type_1, event_type_2
But that only gives me this:
+------------+------------+-----+
|event_type_1|event_type_2|count|
+------------+------------+-----+
|1 |1 |1 |
|1 |0 |1 |
|0 |1 |2 |
+------------+------------+-----+
You can use a lateral join to unpivot the data. Then use conditional aggregate to calculate m and f:
select v.which,
count(*) filter (where d.sex = 'm') as m,
count(*) filter (where d.sex = 'f') as f
from d cross join lateral
(values (d.event_type_1, 'event_type_1'),
(d.event_type_2, 'event_type_2')
) v(val, which)
where v.val = 1
group by v.which;
Here is a db<>fiddle.

Trying to fuse row when with same "name"

In SQL I am trying to get two rows to fuse when their name is the same.
Right now I have a SQL query that looks like this:
SELECT TABLE.Name, SUM(TABLE.Value) AS VALUE1, 0 AS VALUE2
FROM TABLE
WHERE TABLE.Bool = true
GROUP BY TABLE.Name
SELECT TABLE.Name, 0 AS VALUE1, SUM(TABLE.Value) AS VALUE2
FROM TABLE
WHERE TABLE.Bool = false
GROUP BY TABLE.Name
Which give me a result lookin like this :
|Name |Value1 |Value2 |
------------------------------
|Name1 |1000 |0 |
|Name2 |2000 |0 |
|Name3 |3000 |0 |
|Name |Value1 |Value2 |
------------------------------
|Name1 |0 |0001 |
|Name2 |0 |0002 |
|Name3 |0 |0003 |
Using the UNION operator would yield a result such as this:
|Name |Value1 |Value2 |
------------------------------
|Name1 |1000 |0 |
|Name2 |2000 |0 |
|Name3 |3000 |0 |
|Name1 |0 |0001 |
|Name2 |0 |0002 |
|Name3 |0 |0003 |
The result I'd like to obtain is something like this:
|Name |Value1 |Value2 |
------------------------------
|Name1 |1000 |0001 |
|Name2 |2000 |0002 |
|Name3 |3000 |0003 |
Note :
Due to the way the table is built, both selects can return a different number of rows.
A bit of precision about the construction of the table :
The displayed column Value1 and Value 2 both come from the same column, of the same table, and use the same dataset. The only difference is that sometime a condition is met and other time not.
The table can have several fields with the same name, but we want to display only one result for each name.
I cannot modify the table, or its structure in any way, shape or form, even if I think the structure could be improved a lot.
If anyone knows of a way to do this, it would help a lot.
You can use conditional aggregation:
SELECT TABLE.Name,
SUM(case when TABLE.Bool = true then TABLE.Value else 0 end) AS VALUE1,
SUM(case when TABLE.Bool = false then TABLE.Value else 0 end) AS VALUE2
FROM TABLE
GROUP BY TABLE.Name
VALUE1 returns the sum of Value when the condition is met, whereas VALUE2 returns the sum of Value for the rest of the records.

Joining with null

I have two tables and I want to join them with null values in it.
Sample data of my first table(A_TEST):
+--+----+
|ID|NAME|
+--+----+
| |a |
|1 |b |
|1 |c |
+--+----+
Sample data of my second table(B_TEST):
+--+----+
|ID|NAME|
+--+----+
|1 |d |
|2 |e |
|3 |f |
+--+----+
I need to achieve the result by joining a_test.id = b_test.id and if there is null values in it I need to fetch them too. So I tried to write query as below,
select a_test.id,a_test.name,b_test.id,b_test.name
from a_test,b_test
where (a_test.id = b_test.id
or a_test.id is null);
I got output as below,
+--+----+--+----+
|ID|NAME|ID|NAME|
+--+----+--+----+
| |a |1 |d |
| |a |2 |e |
| |a |3 |f |
|1 |b |1 |d |
|1 |c |1 |d |
+--+----+--+----+
But my expected result is, since id 1 is there in my a_test i need the corresponding row from b_test also.See output below
+--+----+--+----+
|ID|NAME|ID|NAME|
+--+----+--+----+
| |a |1 |d |
|1 |b |1 |d |
|1 |c |1 |d |
+--+----+--+----+
I tried with outer joins also but that also does not give me the expected output.
Your own query is almost correct (although you shouldn't use error-prone comma-separated joins that went out of fashion some twenty years ago). You are only missing the condition what must match in case of a_test.id is null (which is: the b_test.id must be in table a_test).
select
a.id as a_id,
a.name as a_name,
b.id as b_id,
b.name as b_name
from a_test a
join b_test b on
(a.id = b.id)
or
(a.id is null and b.id in (select id from a_test));
SQL fiddle: http://www.sqlfiddle.com/#!4/fae22/2.
However strange and meaningless your requirement is, this query gives you your expected result:
select A.*, B.*
from a_test A
join b_test B
on A.id = B.id
union all
select A.*, B.*
from a_test A
cross join b_test B
where A.id is null
and exists (
select 1
from a_test Ax
where Ax.id = B.id
)
order by 2, 4
;
Enjoy!
If a a_test.id NULL is supposed to be treated as 1 when joining, use COALESCE and a sub-query to find replacing value (to be figured out by yourself, just make sure it doesn't return more than one row):
select a_test.id,a_test.name,b_test.id,b_test.name
from a_test,b_test
where COALESCE(a_test.id,(select integervalue from sometable)) = b_test.id
select a_test.id,a_test.name,b_test.id,b_test.name
from a_test,b_test
where a_test.id = b_test.id(+)
But, what are you want see, when a_test.id is null or missing?