Transact-SQL: Display two SUM() from differents tables? - sql

This is a simplified exemple of what I want :
Table 1 :
CODE | VALUE
A | 10
A | 20
B | 10
C | 20
Table 2 :
CODE | VALUE2
A | 25
B | 10
B | 10
D | 20
And this is what I want :
CODE | SUM(VALUE) | SUM(VALUE2)
A | 30 | 25
B | 10 | 20
C | 20 | NULL
D | NULL | 20
I tried naively :
SELECT T1.CODE, SUM(VALUE), SUM(VALUE2)
FROM table1 T1
LEFT OUTER JOIN table2 T2
ON T1.CODE = T2.CODE
GROUP BY T.CODE
But the results are wrong and I don't know what to do... Someone can explain me how to resolve this problem and create a proper query ?

May be something like this?
select code, sum(v1), sum(v2)
from (select code, value v1, null v2
from table1
union
select code, null v1, value2 v2
from table2)
group by code

Related

behavior of filters in outer join

I understand filters in JOIN clause and in WHERE clause is different when using outer join. Let's say I have these 2 tables.
table1
id | value
---+------
1 | 11
2 | 12
table2
id | value
---+------
1 | 101
Now if I query for
select a.id as id1, a.value as value1, b.value as value2
from table1 as a
left join table2 on a.id=b.id and a.value=11
The result is this, an extra row with value1=12
id1 | value1 | value2
----+--------+--------
1 | 11 | 101
2 | 12 | NULL
However, if I put the filter in where clause, it gives me what I want. The question is why it behaves like this?
The second condition used on your left join example limits which rows will be considered for joining.
select f1.id as id1, t1.value as value1, t2.value as value2
from t1
left join t2 on t1.id=t2.id AND T2.VALUE=11
t1
id | value
---+------
1 | 11 ONLY join on this row because t1.value=11
2 | 12
t2
id | value
---+------
1 | 101 this has t1.id=t2.id, so it does get joined
which would produce this final result:
id1 | value1 | value 2
----+--------+--------
1 | 11 | 101
2 | 12 | NULL
Moving the predicate T2.VALUE=11 to the where clause has a different series of events, as follows:
select f1.id as id1, t1.value as value1, t2.value as value2
from t1
left join t2 on t1.id=t2.id
WHERE T2.VALUE=11
t1
id | value
---+------
1 | 11 this row does meet t1.id=t2.id, so it gets joined
2 | 12 this row does NOT meet t1.id=t2.id, FAILS to join
t2
id | value
---+------
1 | 101 this row does meet t1.id=t2.id, so it gets joined
which would produce this INTERIM result:
id1 | value1 | value 2
----+--------+--------
1 | 11 | 101
2 | 12 | NULL
NOW the where clause is considered
id1 | value1 | value 2
----+--------+--------
1 | 11 | 101 T2.VALUE does equal 11 so this row will be returned
2 | 12 | NULL T2.VALUE does NOT = 11 so this row is NOT returned
Thus the final result is:
id1 | value1 | value 2
----+--------+--------
1 | 11 | 101

Query for unique values

I have the following database table in Access:
Field1 | Field2
A | 1
B | 1
C | 2
D | 2
B | 3
O | 3
L | 3
I want to develop a query in Access (preferably without using SQL) to select all values in Field2 corresponding to an occurence of the value "B" in field 1. This query should yield
Field1|Field2
A | 1
B | 1
B | 3
O | 3
L | 3
Use a subquery:
select t.*
from t
where t.field2 in (select t2.field2 from t as t2 where t2.field1 = 'B');

Ordering results by values in other table

I have two tables :
Table1
id | name | age | d_o_b
=====================================
1 | ASD | 22 | 12/01/1992
2 | QWE | 21 | 04/04/1993
3 | FRG | 24 | 04/04/1990
Table2
id | age
===============
1 | 22
2 | 21
3 | 24
Is it possible to order by two columns one from first Table1 and then by one column from Table2.
Something like ..
SELECT * FROM Table1 order by d_o_b , age in (SELECT * FROM Table2)
To order by a column in other table you may need to join them. This should work:
SELECT a.* FROM Table1 a
join table2 b
on a.id=b.id
order by d_o_b,b.age

Is there a workaround to the Oracle Correlated Subquery Nesting Limit?

I have a situation where I'm trying to use a correlated subquery but am running into the nesting limit in Oracle. I might be missing another feature that Oracle has, so I thought I'd post this question here. Does anyone know how to rewrite the below SQL without running into this nesting limit, but also staying within the below constraints?
Constraints:
Only the SQL in the IN clause can be modified (Due to constraints beyond my control)
As shown, the filtering in the parent query needs to be applied to the aggregation subquery before the aggregation occurs.
Filter out 0 on an aggregation of colB after the parent filter is applied
The below code shows my try at this before running into the Oracle limit. Also, the Oracle version I'm on is 11.2.0.2. Any help would be appreciated. Thanks!
SELECT
*
FROM
table1 t1
WHERE
t1.colA BETWEEN XXXX AND XXXX
AND t1.pk_id IN (
SELECT
t2.pk_id
FROM (
SELECT
t3.pk_id,
SUM(t3.amt) OVER (PARTITION BY t3.colB) amt
FROM table1 t3
WHERE t3.colA = t1.colA
) t2
WHERE
t2.amt <> 0
)
Here are some sample input/outputs of what I was looking for when running the above SQL:
Sample table1:
-----------------------------
| pk_id | colA | colB | amt |
-----------------------------
| 1 | 1 | A | 2 |
| 2 | 1 | A | -1 |
| 3 | 1 | B | 1 |
| 4 | 2 | B | 1 |
| 5 | 2 | A | -2 |
| 6 | 2 | A | 1 |
| 7 | 3 | A | 1 |
Results of SUM over t3.colB with t1.colA BETWEEN 1 And 2:
---------------
| pk_id | amt |
---------------
| 1 | 0 |
| 2 | 0 |
| 3 | 2 |
| 4 | 2 |
| 5 | 0 |
| 6 | 0 |
Results of subquery for IN clause with t1.colA BETWEEN 1 And 2:
---------
| pk_id |
---------
| 3 |
| 4 |
Result of top level query with t1.colA BETWEEN 1 And 2:
-----------------------------
| pk_id | colA | colB | amt |
-----------------------------
| 3 | 1 | B | 1 |
| 4 | 2 | B | 1 |
After working through some of the answers provided, I have a way of avoiding the nesting limit in Oracle with a simple CASE statement:
SELECT
*
FROM
table1 t1
WHERE
t1.colA BETWEEN 1 AND 2
AND t1.pk_id IN (
SELECT
CASE
WHEN SUM(t2.amt) OVER (PARTITION BY t2.colB) <> 0 THEN t2.pk_id
ELSE NULL
END
FROM table1 t2
WHERE t2.colA = t1.colA
)
Unfortunately this surfaced the real problem. Because this is a subquery, I can only iterate through one value of the t1.colA range at a time. This appears to make it impossible execute the analytic sum within that range in the subquery. Because I can only modify the SQL within the IN clause, I don't see a solution to this problem. If anyone has any suggestions please let me know. Thanks.
If you know what the between values are and can use those in your subquery, then you can add that to your subquery instead:
SELECT
*
FROM
table1 t1
WHERE
t1.colA BETWEEN 1 AND 2
AND t1.pk_id IN (
SELECT
t2.pk_id
FROM
(
SELECT
t3.pk_id,
SUM(t3.amt) OVER (PARTITION BY t3.colB) amt
FROM table1 t3
WHERE t3.colA BETWEEN 1 AND 2
) t2
WHERE
t2.amt <> 0
)
SQL Fiddle Demo
You can rewrite your query like this:
SELECT *
FROM table1 t1
WHERE t1.colA BETWEEN XXXX AND XXXX and
t1.pk_id IN (
SELECT t2.pk_id
FROM (SELECT t3.pk_id, t3.ColA, SUM(t3.amt) as amt
FROM table1 t3
group by t3.pk_id, t3.ColA
having sum(t3.amt) > 0
) t2
WHERE t2.colA = t1.colA
)
From here, you can rewrite it as:
select t1.*
from table1 t1 join
(SELECT t3.pk_id, t3.ColA, SUM(t3.amt) as amt
FROM table1 t3
group by t3.pk_id, t3.ColA
having sum(t3.amt) > 0
) t2
on t1.pk_id = t2.pk_id and t1.ColA = t3.ColA
WHERE t1.colA BETWEEN XXXX AND XXXX

When joining a table to itself to identfy duplicate date in a column, how do you keep it from returning the inverse in the results?

I am trying to write a sql statement to return me the list of duplicate items I find in a table. For the sake of simplicity imagine a table named TEST with a rowid column and a text column called column 1 with the following date:
rowid | column1
---------------
1 | A
2 | B
3 | C
4 | A
5 | B
6 | C
7 | D
The query I currently have is:
select t1.rowid, t1.column1, t2.rowid, t2.column1
from test t1
inner join test t2 on t1.column1 = t2.column1 and t1.rowid <> t2.rowid
It gives me the following results, as I would expect it to do:
rowid | column1 | rowid | column1
---------------------------------
1 | A | 4 | A
2 | B | 5 | B
3 | C | 6 | C
4 | A | 1 | A
5 | B | 2 | B
6 | C | 3 | C
What I really want is just:
rowid | column1 | rowid | column1
---------------------------------
1 | A | 4 | A
2 | B | 5 | B
3 | C | 6 | C
What black sql magic to I need to call upon in order to get my desired result?
select t1.rowid, t1.column1, t2.rowid, t2.column1
from test t1
inner join test t2 on t1.column1 = t2.column1 and t1.rowid < t2.rowid
Another approach to produce results in the same form as the original table:
SELECT t.rowid, t.column1
FROM (SELECT column1
FROM test
GROUP BY column1
HAVING COUNT(*) > 1) q
INNER JOIN test t
ON q.column1 = t.column1
ORDER BY t.column1, t.rowid
Have you tried this?
select min(rowid), column1, max(rowid), column1
from test
group by column1
having count(*)>1
Saves doing self-joins or subqueries, gotta be faster.