SQLite check if subquery gives same answers as other subquery - sql

I need to construct query like this:
SELECT * FROM table1 AS t1 WHERE (
(SELECT column2 FROM table1 WHERE column1=t1.column1)!=(SELECT column2 FROM table1 WHERE column1=1)
)
But the problem is that SQLite checks only first results of subqueries.
I mean that if SELECT column2 FROM table1 WHERE column1=t1.column1 gives following results: (1,2,3) and SELECT column2 FROM table1 WHERE column1=1 gives (1,2,3,4) SQLite will check only 1!=1, not (1,2,3)!=(1,2,3,4).
I need to filter rows where prevoius two queries give only same rows (i.e. (1,2,3) from the first and (1,2,3) from the second)
The example of my table:
id | column1 | column2 | ...
1 | 1 | 1 | ...
2 | 1 | 2 | ...
3 | 1 | 3 | ...
4 | 2 | 1 | ...
5 | 2 | 2 | ...
6 | 2 | 3 | ...
7 | 2 | 4 | ...
8 | 3 | 1 | ...
9 | 3 | 2 | ...
10 | 3 | 3 | ...
And I need to get rows from id=4 to id=7 because it has 7 | 2 | 4 | ... row (for one column1 value there are not same column2 values as in column1)

You could try compound select statements: subtract the subqueries from each other and check if any row is left:
SELECT *
FROM table1 AS t1
WHERE EXISTS (SELECT column2 FROM table1 WHERE column1=t1.column1
EXCEPT
SELECT column2 FROM table1 WHERE column1=1)
OR EXISTS (SELECT column2 FROM table1 WHERE column1=1
EXCEPT
SELECT column2 FROM table1 WHERE column1=t1.column1)
Alternatively, do a similar operation with individual rows: the subqueries are different if some row does not have a counterpart in the other subquery:
SELECT *
FROM table1 AS t1
WHERE EXISTS (SELECT column2
FROM table1 AS t2
WHERE column1 = t1.column1
AND NOT EXISTS (SELECT 1
FROM table1 AS t3
WHERE column2 = t2.column2
AND column1 = 1))
OR EXISTS (SELECT column2
FROM table1 AS t2
WHERE column1 = 1
AND NOT EXISTS (SELECT 1
FROM table1 AS t3
WHERE column2 = t2.column2
AND column1 = t1.column1))
(The second query might be more efficient if you have the requisite indexes.)

Related

How to find rows that have only one value in another table

I want to find rows from table 1 that are joining with table 2 and have all rows same in table 2.
Example:
Row with id 4 in table2 is not valid because have different values in table1 (value1, value)
Row with id 5 in table2 is valid because have same values in table1 (value3)
table1
+----+--------+----------+
| id |table2Id| value |
+----+--------+----------+
| 1 | 4 |value1 |
| 2 | 4 |value2 |
| 3 | 5 |value3 |
| 4 | 5 |value3 |
| 5 | 5 |value3 |
+----+--------+----------+
table2
+----+
| id |
+----+
| 4 |
| 5 |
+----+
I'm not sure why a join is necessary. You can just use the information in table1:
select t1.*
from (select t1.*,
min(value) over (partition by table2id) as min_value,
max(value) over (partition by table2id) as max_value
from table1 t1
) t1
where min_value = max_value;
Or use not exists:
select t1.*
from table1 t1
where not exists (select 1
from table1 tt1
where tt1.table2id = t1.table2id and
tt1.value = t1.value
);
In either case, you can join to table2 if you need to for filtering or for other columns, but based on the information in your question the join is not needed.
You can do the following:
SELECT * FROM (
SELECT max(table2Id) as Id,value
FROM table1
GROUP BY value
HAVING COUNT(value)=1) JOIN table2 on table1.Id = table2.Id
Thanks
Try this -
Select t1.* from table1 t1 JOIN table2 t2 on t1.id = t2.id and t1.table2Id = t2.id

GROUP BY Aggregation with COUNT

I would like to know how to join tables in order to get a sort of a pivot.
table1 table2
col1 | col2 col1
------+------ -----
1 | A A
1 | B B
1 | C C
2 | A D
2 | A E
2 | A
2 | B
This code will return first two columns as I would like to (It will list every table2 entry for each table1 entry grouped) but I don't know how to continue to get the count for how many col2 occurancies are in table1. I would like to list zeros as well.
SELECT table1.col1, table2.col1
FROM table1, table2
GROUP BY table1.col1, table2.col1;
Expected result:
col1 | col2 | col3
-------+-------+----
1 | A | 1
1 | B | 1
1 | C | 1
1 | D | 0
1 | E | 0
2 | A | 3
2 | B | 1
2 | C | 0
2 | D | 0
2 | E | 0
You can use a Cartesian query:
SELECT
table1.col1,
table2.col1 as col2,
Abs(Sum([table1].[col2]=[table2].[col1])) AS col3
FROM
table1,
table2
GROUP BY
table1.col1,
table2.col1;
You can use such a query with joins, aggregations and correlated subquery as below:
SELECT t1.col1,t1.col2,
(SELECT count(*) FROM table1 WHERE col2=t1.col2 AND col1=t1.col1) as col3
FROM (SELECT t1.col1,max(col2) as col2,count(col2) as ct
FROM table2 t2
JOIN table1 t1
ON t2.col1=t1.col2
GROUP BY t1.col1) t2
RIGHT JOIN ( SELECT tt1.col1,t2.col1 as col2
FROM table2 t2
CROSS JOIN (SELECT distinct col1 FROM table1 ) tt1
) t1
ON t2.col2=t1.col2;
Demo

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

SQL: Inner join with top 2 rows with second table from on condition

Hi I am now trying to join 2 table with only 2 rows from second table join to first table.
For example, I have following 2 tables:
**Table A**
Column1 | Column2 | Column3
A | B | 30
A | C | 50
A | D | 25
**Table B**
Column4 | Column5
B | 35
B | 90
B | 65
B | 80
B | 85
B | 40
C | 100
C | 60
C | 70
C | 65
Here is example of my normal query:
select *
from
(
select *
from A
where Column1 = 'A' and (Column2 = 'B' or Column2 = 'C')
order by Column2, Column3
) A
inner join
(
select *
from B
where (Column4 = 'B' or Column4 = 'C')
order by Column5
) B
on (A.Column2 = B.Column4 and ((B.Column5 - A.Column3) > 30))
The Result should look like:
**Result:**
Column1 | Column2 | Column3 | Column4 | Column5
A | B | 30 | B | 65
A | B | 30 | B | 80
A | B | 30 | B | 85
A | B | 30 | B | 90
A | C | 50 | C | 100
However, the result that I want is to join only 2 row from second table result only. The expected result should be:
**Expected Result:**
Column1 | Column2 | Column3 | Column4 | Column5
A | B | 30 | B | 65
A | B | 30 | B | 80
A | C | 50 | C | 100
Do anyone have idea of how to create such sql statement? Thank you.
You could use row_number() to limit the number of rows. The example assumes that (Column1, Column2, Column3) is unique. If table A has a primary key, use that instead.
select *
from (
select Column1
, Column2
, Column3
, Column4
, Column5
, row_number() over (partition by Column1, Column2, Column3
order by Column5 - Column3 desc) as rn
from A
join B
on A.Column2 = B.Column4
where Column1 = 'A'
and Column2 in ('B', 'C')
and Column5 - Column3 > 30
) SubQueryAlias
where rn < 2
See example at SQL Fiddle.
A good start would be to write more simple SQL without the inline views:
select *
from A inner join
B on (A.Column2 = B.Column4)
where A.Column1 = 'A' and
A.Column2 in ('B','C') and
(B.Column5 - A.Column3) > 30)
Try using a CTE
WITH Top2Rows AS (
SELECT TOP 2 *
FROM TableB
WHERE ([ADD CONSTRAINTS])
)
SELECT *
FROM TableA a
JOIN Top2Rows r ON a.ID = b.ID
WHERE ([ADD CONSTRAINTS])

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.