I have a table like the below
| date | key | value | |
|------------|-----|-------|---|
| 01-01-2009 | a | 25 | |
| 01-01-2009 | b | 25 | |
| 01-01-2009 | c | 10 | |
I'm trying to come up with a query which would allow me to do (a+b)-c for each day - but my join is doing this (a+b+c)-c
with total as (
select
sum(value),
date
from
A
where
key in ('a',
'b')
group by
date )
select
sum(total.sum) as a, sum(A.value) as b,
sum(total.sum) - sum(A.value) as value,
A.date
from
A
join total on
total.date = A.date
where
A.key = 'c'
group by
A.date
This is giving me a value of 50 (it should be 40) - my C values are getting calcualted as part of the total table during the join
What am i doing wrong?
How about simply doing conditional aggregation?
select date,
sum(case when key in ('a', 'b') then value
when key in ('c') then - value
end) as daily_calc
from a
group by date;
A join doesn't seem very helpful for this calculation.
You can join three table expressions, as in:
select
a.date,
(a.value + b.value) - c.value
from (select * from A where key = 'a') a
join (select * from A where key = 'b') b on b.date = a.date
join (select * from A where key = 'c') c on c.date = a.date
Related
The goal is to join all the same values (the duplicates) together. Email, timestamp and daystamp.
I have created one join statement
SELECT history.email, history.timestamp, payment.timestamp,
history.daystamp, payment.daystamp
FROM history
FULL OUTER JOIN payment ON history.email = payment.email
ORDER BY history.email;
I have all the unique email addresses. How do I do the same for the timestamp and daystamp?
Can I do three outer joins in one statement?
Here are two methods that might be useful to adapt to your specific problem here although it's a little unclear without sample data.
Method 1:
SELECT A.column2
, B.column2
, C.column2
FROM
(
(SELECT month, column2 FROM table1) A
FULL OUTER JOIN
(SELECT month, column2 FROM table2) B on A.month= B.month
FULL OUTER JOIN
(SELECT month, column2 FROM table3) C on A.month= C.month
)
Method 2:
select
A.column2,
B.column2,
C.column2
from (
select distinct month from table1
union
select distinct month from table2
union
select distinct month from table3
) as X
left outer join table1 as A on A.month = X.month
left outer join table2 as B on B.month = X.month
left outer join table3 as C on C.month = X.month
Something like this?
SELECT
case when p.payment_id is not null then 'p' else 'h' end as tbl
, coalesce(h.email, p.email) as email
, coalesce(h.timestamp, p.timestamp) as timestamp
, coalesce(h.daystamp, p.daystamp) as daystamp
FROM history h
FULL JOIN payment p
ON h.email = p.email
AND h.timestamp = p.timestamp
AND h.daystamp is not distinct from p.daystamp
WHERE (h.history_id is null or p.payment_id is null)
ORDER BY coalesce(h.email, p.email);
tbl | email | timestamp | daystamp
:-- | :------------- | :------------------ | -------:
h | test2#mail.not | 2022-02-22 22:22:22 | 20220222
p | test2#mail.not | 2022-02-22 22:28:22 | 20220222
h | test3#mail.not | 2022-02-22 22:22:23 | 20220222
p | test4#mail.not | 2022-02-22 22:22:24 | 20220222
Test on db<>fiddle here
I have a table t with three columns, a, b, c. I want to calculate the number of a where b =1 over the number of a where b = 2 for every category in c. some Pseudo code is like: (mysql)
select count(distinct a) where b = 1 / count(distinct a) where b = 2
from t
group by c
but this won't work in SQL, since the condition 'where' cannot add for every category in c in the clause group by c.
You don't mention which database you are using, so I'll assume it implements FULL OUTER JOIN.
Also, you don't say what to do in case a division by zero could happen. Anyway, this query will get you the separate sums, so you can compute the division as needed:
select
coalesce(x.c, y.c) as c
coalesce(x.e, 0) as b1
coalesce(y.f, 0) as b2
case when y.f is null or y.f = 0 then -1 else x.e / y.f end
from (
select c, count(distinct a) as e
from t
where b = 1
group by c
) x
full join (
select c, count(distinct a) as f
from t
where b = 2
group by c
) y on x.c = y.c
You can do this in SQL Server, PostgreSQL, MySQL:
create table test (a int, b int, c varchar(10));
insert into test values
(1, 1, 'food'), (2, 1, 'food'), (3, 1, 'food'),
(1, 2, 'food'), (2, 2, 'food'),
(1, 1, 'drinks'), (2, 1, 'drinks'), (2, 1, 'drinks'),
(1, 2, 'drinks')
;
select cat.c, cast(sum(b1_count) as decimal)/sum(b2_count), sum(b1_count), sum(b2_count) from
(select distinct c from test) as cat
left join
(select c, count(distinct a) b1_count from test where b = 1 group by c) b1 on cat.c = b1.c
left join
(select c, count(distinct a) b2_count from test where b = 2 group by c) b2
on cat.c = b2.c
group by cat.c;
Result
c | (No column name) | (No column name) | (No column name)
:----- | ---------------: | ---------------: | ---------------:
drinks | 2.00000000000 | 2 | 1
food | 1.50000000000 | 3 | 2
Examples:
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=2003e0baa46bfbb197152b829ea57d2d
https://dbfiddle.uk/?rdbms=postgres_11&fiddle=2003e0baa46bfbb197152b829ea57d2d
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=2003e0baa46bfbb197152b829ea57d2d
You can use conditional aggregation:
select count(distinct case when b = 1 then a end) / count(distinct case when b = 2 then a end)
from t
group by c;
You don't mention your database, but some do integer division -- which can result in unexpected truncation. You might want a * 1.0 / instead of / to for non-integer division.
I got table like this:
num |type|value
--------------
1 | a | 5
1 | b | 7
3 | c | 9
2 | a | 6
2 | b | 9
and want this kind of result:
num| value (a) | value (b)
-------------------------
1 | 5 | 7
2 | 6 | 9
You can use a self-join which will also remove the rows with just one value (num = 3 in your sample data)
select t1.num, t1.value as value_a, t2.value as value_b
from the_table t1
join the_table t2 on t1.num = t2.num and t2.type = 'b'
where t1.type = 'a'
You can use GROUP BY and CASE, as in:
select
num,
max(case when type = 'a' then value end) as value_a,
max(case when type = 'b' then value end) as value_b
from t
group by num
I'd join the table on itself, once for a and once for b
SELECT a.num, a.value, b.value
FROM mytable a
JOIN mytable b ON a.num = b.num AND a.type = 'a' AND b.type = 'b'
I have 3 tables. The main one in which I want to retrieve some information and two others for row count only.
I used a request like this :
SELECT A.*,
COUNT(B.id) AS b_count
FROM A
LEFT JOIN B on B.a_id = A.id
WHERE A.id > 50 AND B.ID < 100
GROUP BY A.id
from Gerry Shaw's comment here. It works perfectly but only for one table.
Now I need to add the row count for the third (C) table. I tried
SELECT A.*,
COUNT(B.id) AS b_count
COUNT(C.id) AS c_count
FROM A
LEFT JOIN B on B.a_id = A.id
LEFT JOIN C on C.a_id = A.id
GROUP BY A.id
but, because of the two left joins, my b_count and my c_count are false and equal to each other. In fact my actual b_count and c_count are equal to real_b_count*real_c_count. Any idea of how I could fix this without adding a lot of complexity/subqueries ?
Data sample as requested:
Table A (primary key : id)
id | data1 | data2
------+-------+-------
1 | 0,45 | 0,79
----------------------
2 | -2,24 | -0,25
----------------------
3 | 1,69 | 1,23
Table B (primary key : (a_id,fruit))
a_id | fruit
------+-------
1 | apple
------+-------
1 | banana
--------------
2 | apple
Table C (primary key : (a_id,color))
a_id | color
------+-------
2 | blue
------+-------
2 | purple
--------------
3 | blue
expected result:
id | data1 | data2 | b_count | c_count
------+-------+-------+---------+--------
1 | 0,45 | 0,79 | 2 | 0
----------------------+---------+--------
2 | -2,24 | -0,25 | 1 | 2
----------------------+---------+--------
3 | 1,69 | 1,23 | 0 | 1
There are two possible solutions. One is using subqueries behind SELECT
SELECT A.*,
(
SELECT COUNT(B.id) FROM B WHERE B.a_id = A.id AND B.ID < 100
) AS b_count,
(
SELECT COUNT(C.id) FROM C WHERE C.a_id = A.id
) AS c_count
FROM A
WHERE A.id > 50
the second are two SQL queries joined together
SELECT t1.*, t2.c_count
FROM
(
SELECT A.*,
COUNT(B.id) AS b_count
FROM A
LEFT JOIN B on B.a_id = A.id
WHERE A.id > 50 AND B.ID < 100
GROUP BY A.id
) t1
JOIN
(
SELECT A.*,
COUNT(C.id) AS c_count
FROM A
LEFT JOIN C on C.a_id = A.id
WHERE A.id > 50
GROUP BY A.id
) t2 ON t1.id = t2.id
I prefer the second syntax since it clearly shows the optimizer that you are interested in GROUP BY, however, the query plans are usually the same.
If tables B & C also have their own key fields, then you can use COUNT DISTINCT on the primary key rather than foreign key. That gets around the multi-line problem you see on link to several tables. If you can post the table structures then we can advise further.
Try something like this
SELECT A.*,
(SELECT COUNT(B.id) FROM B WHERE B.a_id = A.id) AS b_count,
(SELECT COUNT(C.id) FROM C WHERE C.a_id = A.id) AS c_count
FROM A
That is the easier way I can think:
Create table #a (id int, data1 float, data2 float)
Create table #b (id int, fruit varchar(50))
Create table #c (id int, color varchar(50))
Insert into #a
SELECT 1, 0.45, 0.79
UNION ALL SELECT 2, -2.24, -0.25
UNION ALL SELECT 3, 1.69, 1.23
Insert into #b
SELECT 1, 'apple'
UNION ALL SELECT 1, 'banana'
UNION ALL SELECT 2, 'orange'
Insert into #c
SELECT 2, 'blue'
UNION ALL SELECT 2, 'purple'
UNION ALL SELECT 3, 'orange'
SELECT #a.*,
(SELECT COUNT(#b.id) FROM #b where #b.id = #a.id) AS b_count,
(SELECT COUNT(#c.id) FROM #c where #c.id = #a.id) AS b_count
FROM #a
ORDER BY #a.id
Result:
id data1 data2 b_count b_count
1 0,45 0,79 2 0
2 -2,24 -0,25 1 2
3 1,69 1,23 0 1
If table b and c have unique id, you can try this:
SELECT A.*,
COUNT(distinct B.fruit) AS b_count,
COUNT(distinct C.color) AS c_count
FROM A
LEFT JOIN B on B.a_id = A.id
LEFT JOIN C on C.a_id = A.id
GROUP BY A.id
See SQLFiddle MySQL demo.
Following query is giving me invalid identifier error (a.id) because it is inside nested subquery:
SELECT a.*,
CASE WHEN
SELECT id FROM (SELECT id, ROWNUM rnum FROM US b WHERE b.id = a.id ORDER BY b.createdate ASC) WHERE rnum = 2) = 21 THEN ‘Found’
END SEARCH
FROM EU a
JOIN US b ON b.id = a.id;
Can anyone suggest me alternate approach?
You haven't described much of your set-up or what you are trying to achieve but will this solve it?
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE EU ( id ) AS
SELECT 19+LEVEL
FROM DUAL CONNECT BY LEVEL <= 5;
CREATE TABLE US ( id, createdate ) AS
SELECT 19+LEVEL, SYSDATE - LEVEL
FROM DUAL CONNECT BY LEVEL <= 5
UNION ALL
SELECT 19+2*LEVEL, SYSDATE-LEVEL-5
FROM DUAL CONNECT BY LEVEL <= 3;
Query 1:
SELECT a.*,
CASE WHEN a.id = 21
AND ROW_NUMBER() OVER ( PARTITION BY a.id ORDER BY b.createdate ) = 2
THEN 'Found'
END AS SEARCH
FROM EU a
JOIN US b
ON b.id = a.id
Results:
| ID | SEARCH |
|----|--------|
| 20 | (null) |
| 21 | (null) |
| 21 | Found |
| 22 | (null) |
| 23 | (null) |
| 23 | (null) |
| 24 | (null) |
One problem with your query is the lack of paren before the second SELECT. I still don't think it will work, because Oracle limits the scope of an identifier to one layer deep. But this is what I think you intend:
SELECT a.*,
(CASE WHEN (SELECT id
FROM (SELECT id, ROWNUM as rnum
FROM US b
WHERE b.id = a.id
ORDER BY b.createdate ASC
)
WHERE rnum = 2
) = 21 THEN ‘Found’
END) SEARCH
FROM EU a JOIN
US b
ON b.id = a.id;
The easiest way to get what you want is to use row_number():
select a.*,
(case when b.id = 21 then 'Found' end) as Search
from eu a left join
(select b.*,
row_number() over (partition by b.id order by b.createddate) as seqnum
from us b
) b
on b.id = a.id
where seqnum = 2;
The query seems strange because id is used for everything. You seem to be looking for two records on id 21.