Combining the result of two queries using SQLite - sql

Let's say I have the following tables:
Table A Table B
| a | b | c | | a | b | c |
|---|---|---| |---|---|---|
| a | b | c | | a | b | c |
| a | c | d | | a | c | d |
| c | d | e | | a | b | c |
| f | g | h | | o | p | q |
| a | a | a | | a | b | c |
and I want to find all the rows that differ from both tables.
This gives me the rows from A that are not in B:
SELECT a, b, c FROM A
EXCEPT
SELECT a, b, c FROM B;
| a | b | c |
|---|---|---|
| a | a | a |
| c | d | e |
| f | g | h |
and this gives me the rows from B that are not in A:
SELECT a, b, c FROM B
EXCEPT
SELECT a, b, c FROM A;
| a | b | c |
|---|---|---|
| o | p | q |
So to combine the two I tried using
SELECT a, b, c FROM A
EXCEPT
SELECT a, b, c FROM B
UNION
SELECT a, b, c FROM B
EXCEPT
SELECT a, b, c FROM A;
but it still only gives me the rows from B.
How do I get rows from both tables, like this?
| a | b | c |
|---|---|---|
| a | a | a |
| c | d | e |
| f | g | h |
| o | p | q |

Try enclosing the individual except in parentheses and then do the union:
select * from (
select a, b, c from a
except
select a, b, c from b
)
union
select * from (
select a, b, c from b
except
select a, b, c from a
);

Related

Sum with Loop expression

I want to make a calculation over several lines and as a result there should be one result
Table g
| id |Oppervlakte|
| 1 | 10 |
| 2 |12 |
| 3 | 7 |
| 4 | 8 |
table d
| id | gid | Oppervlakte |
| 1 | 1 | 2 |
| 1 | 2 | 3 |
| 1 | 2 | 2 |
| 1 | 2 | 2 |
| 1 | 3 | 1 |
And the result
| id |test |
| 1 | 216 |
my code now is
Select r.id,
sum((g.oppervlakte-sum(d.oppervlakte)*8) as 'test'
from r
join g on g.rid=r.id
join d on d.gid=g.id
join c on c.id = g.Id
where c.cType=0
Group by r.id, g.oppervlakte
You probably just want to aggregate twice, once in a sub-query...
SELECT
r.id,
SUM((g.oppervlakte - d.oppervlakte) * 8) as 'test'
FROM
r
INNER JOIN
g
ON g.rid = r.id
INNER JOIN
(
SELECT
d.gid,
SUM(d.oppervlakte) AS oppervlakte
FROM
d
GROUP BY
d.gid
)
d
ON d.gid = g.id
INNER JOIN
c
ON c.id = g.id
WHERE
c.cType = 0
GROUP BY
r.id

SQL Get average of values in left table if there is record in B table

I'm trying to average records in table A if there is at least 1 matching record in table B:
SELECT AVG ( A.w )
FROM A
INNER JOIN B on A.pk = B.fk
WHERE B.v = 5
Based on the sample data described below, I would expect a result of 50 i.e. there are 6 records in B where v = 5 and fk = A, and one record where v = 5 and fk = B, so I'm getting 85.714...
Thank you in advance for your wisdom!
Values in Table A
+---+---+
|pk | w |
+---+---+
| A |100|
| B | 0 |
| C |500|
| D | 5 |
+---+---+
Table B
+---+---+
|fk | v |
+---+---+
| A | 5 |
| A | 5 |
| A | 5 |
| A | 10|
| A | 5 |
| A | 5 |
| A | 5 |
| B | 5 |
| C | 9 |
| D | 7 |
+---+---+
Use exists:
SELECT AVG( A.w )
FROM A
WHERE EXISTS (SELECT 1 FROM B WHERE A.pk = B.fk AND B.v = 5);
You are not taking into account that the JOIN is multiplying the number of rows in A that are used for the average.
For performance you want an index on B(fk, v).

group by and select non null value if present

Is there a way I can perform group by and use non value for a column if any. i.e
a | b | c | d | e | f |
---------------------------------------------------
1 | 2 | 3 | x | test1 | 2019-07-01 07:17:01 |
1 | 2 | 3 | NULL | test2 | 2019-07-01 10:23:11 |
1 | 2 | 3 | NULL | test3 | 2019-07-01 22:00:51 |
1 | 2 | 7 | NULL | testTet | 2019-07-01 23:00:00 |
In my case above if d is present for say a=1,b=2,c=3 it will always be x otherwise it can come null. So my query would be like
select a,
b,
c,
d,
count(distinct e) as something
from tableX
where f between '2019-07-01 00:00:00' and '2019-07-01 23:59:59.999'
group by a,
b,
c,
d
the results would be:
a | b | c | d | something |
------------------------------|
1 | 2 | 3 | x | 1 |
1 | 2 | 3 | NULL | 2 |
1 |2 | 7 | NULL | 1 |
whereas it will be wonderful if I can have (since for each group by combination I know it's null or that unique value if present):
a | b | c | d | something |
------------------------------|
1 | 2 | 3 | x | 3 |
1 | 2 | 7 | NULL | 1 |
From your sample data I think that you don't need d in the group by clause.
So get its max:
select
a, b, c,
max(d) d,
count(distinct e) as something
from tableX
where f between '2019-07-01 00:00:00' and '2019-07-01 23:59:59.999'
group by a, b, c
try like below
with cte as (select a,
b,
c,
d,
count(distinct e) as something
from tableX
where f between '2019-07-01 00:00:00' and '2019-07-01 23:59:59.999'
group by a,
b,
c,
d) select a,b,c,max(d) as d,sum(something) from cte group by a,b,c

Flatten multiple arrays with uneven lengths in BigQuery

I'm trying to flatten arrays in different columns with different lengths without duplicating the results.
For example (using standard SQL):
WITH
x AS (
SELECT
ARRAY[1,
2,
3] AS a,
ARRAY[1,
2] AS b)
SELECT
a,
b
FROM
x,
x.a,
x.b
Produces:
+-----++-----+
| a | b |
+-----++-----+
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 2 | 2 |
| 3 | 1 |
| 3 | 2 |
+-----++-----+
It should look like this:
+-----++-----+
| a | b |
+-----++-----+
| 1 | 1 |
| 2 | 2 |
| 3 | null |
+-----++-----+
You can use JOIN:
SELECT a, b
FROM x LEFT JOIN
UNNEST(x.a) a left join
unnest(x.b) b
ON a = b;

Join a table representing a "transfer" between two rows of another table

Sorry for the confusing title; however a description and illustration should hopefully clear it up.
Essentially, I have the table A representing instances of a transfer of an 'amount' between rows of table B. I wish to join A with B so that I can display the details of the transfer:
================= A ===================
+-----+-----------+----------+--------+
| AID | fromID(FK) | toID(FK) | amount |
+-----+-----------+----------+--------+
| 1 | 1 | 5 | 100 |
| 2 | 1 | 3 | 150 |
| 3 | 5 | 3 | 500 |
| 4 | 1 | 5 | 200 |
| 5 | 4 | 5 | 800 |
| 6 | 3 | 5 | 15 |
+----+------------+----------+--------+
and
==== B =====
+----+------+
| BID | name |
+----+------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
| 5 | e |
+----+------+
I wish to join them and produce a "from name" column and a "to name" like:
+-----+------+----+--------+
| AID | from | to | amount |
+-----+------+----+--------+
| 1 | a | e | 100 |
| 2 | a | c | 150 |
| 3 | e | c | 500 |
| 4 | a | e | 200 |
| 5 | d | e | 800 |
| 6 | c | e | 15 |
+-----+------+----+--------+
You can join a on b twice:
SELECT aid, from_b.name, to_b.name, amount
FROM a
JOIN b from_b ON from_b.bid = a.fromid
JOIN b to_b ON to_b.bid = a.toid
Do a JOIN between the tables like below but you will have to join table B twice
select a.AID,
b.name as [from],
b1.name as [to],
a.amount
from A a
join B b on a.fromID(FK) = b.BID
join B b1 on a.toID(FK) = B.bid;
You can do this with out join.
Fiddle with sample data
select aid,
(select name from b where a.fromid = bid) as "from",
(select name from b where a.toid = bid) as "to",
amount
from a