MariaDB: get the intersection of two queries results in two tables - sql

So I have two tables, both have the same structure:
tableA
tableB
ID
ID
1
1
3
2
5
3
10
5
What I need is to compare tableA.ID and tableB.ID and find out which IDs are free in both table and get the intervals of those free IDs.
To find out free IDs in one table I use this:
SELECT
a.ID + 1 start,
min(b.ID) - 1 end,
min(b.ID) - a.ID - 1 gap
FROM
tableA a,
tableB b
WHERE a.ID < b.ID
GROUP BY a.ID
HAVING start < MIN(b.ID)
It works fine, I get my gaps:
tableA
start
end
gap
2
2
1
4
4
1
6
9
4
tableB
start
end
gap
4
4
1
But then I need to compare the results and check which IDs are free in both tables. The result should look something like this:
start
end
gap
4
4
1
6
9
4
And here I'm honestly lost and I don't have any ideas what to do. I've tried to use join in, but it doesn't work for me:
WHERE a.ID < b.ID AND a.ID IN (
SELECT
c.ID+1 startID,
min(d.ID) - 1 endID,
min(d.ID) - c.ID - 1 gap
from
tableB c,
tableB d
where c.rowid < d.rowid
)

You may union the two tables to get the unique values from both, then use LEAD function to find the free IDs as the following:
WITH reserved_IDs AS
(
SELECT ID FROM tableA
UNION
SELECT ID FROM tableB
UNION SELECT 0 /* To start from 1 in case 1 value is missing from both tables*/
)
SELECT start_id, end_id, gap
FROM
(
SELECT ID+1 AS start_id,
LEAD(ID) OVER (ORDER BY ID) - 1 AS end_id,
LEAD(ID) OVER (ORDER BY ID)-ID-1 gap
FROM reserved_IDs
) T
WHERE gap > 0
See a demo.

Related

How to generate loop kind of behaviour in SQL query to fetch multiple queries & compare results?

I am unable to generate a looping kind of behaviour in a SQL query.
I am having two tables:
Table A
Id Brand Prod_Id Alt_Prod_Id
1 A 2 5
2 B 3 9
3 C 5 9
Table B
Id Prod_Id Rate
1 2 5
2 3 9
2 5 7
2 9 9
Rate in Table B needs to be looked up for each brands Prod_ID & Alt_Prod_Id & select the least value between 2 found value
The expected result / output is:
Brand Min_Prod_Val
A 5
B 9
C 7
Can this be done in a query?
Thanks!
You could join tableb twice (once for prod_id, another for alt_prod_id), and then select the smallest rate:
select
a.brand,
least(b1.rate, b2.rate) min_prod_val
from tablea a
inner join tableb b1 on b1.prod_id = a.prod_id
inner join tableb b2 on b2.prod_id = a.alt_prod_id
It is unclear which database you are using. If that's SQL Server: it does not support least(), so you need a case expression:
case when b1.rate < b2.rate then b1.rate else b2.rate end min_prod_val
You can use a single join and GROUP BY the brand:
SELECT a.Brand,
MIN( b.rate ) AS min_prod_val
FROM TableA A
INNER JOIN TableB b
ON ( b.prod_id IN ( a.prod_id, a.alt_prod_id ) )
GROUP BY a.Brand
Or you can use a correlated sub-query:
SELECT a.Brand,
(
SELECT MIN( rate )
FROM TableB b
WHERE b.prod_id IN ( a.prod_id, a.alt_prod_id )
) AS min_prod_val
FROM TableA A
db<>fiddle

Selecting nth top row based on number of occurrences of value in 3 tables

I have three tables let's say A, B and C. Each of them has column that's named differently, let's say D1, D2 and D3. In those columns I have values between 1 and 26. How do I count occurrences of those values and sort them by that count?
Example:
TableA.D1
1
2
1
1
3
TableB.D2
2
1
1
1
2
3
TableC.D3
2
1
3
So the output for 3rd most common value would look like this:
3 -- number 3 appeared only 3 times
Likewise, output for 2nd most common value would be:
2 -- number 2 appeared 4 times
And output for 1st most common value:
1 -- number 1 appeared 7 times
You probably want :
select top (3) d1
from ((select d1 from tablea ta) union all
(select d2 from tableb tb) union all
(select d3 from tablec tc)
) t
group by d1
order by count(*) desc;
SELECT DQ3.X, DQ3.CNT
(
SELECT DQ2.*, dense_rank() OVER (ORDER BY DQ2.CNT DESC) AS RN
(SELECT DS.X,COUNT(DS.X) CNT FROM
(select D1 as X FROM TableA UNION ALL SELECT D2 AS X FROM TABLE2 UNION ALL SELECT D3 AS X FROM TABLE3) AS DS
GROUP BY DS.X
) DQ2
) DQ3 WHERE DQ3.RN = 3 --the third in the order of commonness - note that 'ties' can be handled differently
One of the things about SQL scripts: they get difficult to read very easily. I'm a big fan of making things as readable as absolute possible. So I'd recommend something like:
declare #topThree TABLE(entry int, cnt int)
select TOP 3 entry,count(*) as cnt
from
(
select d1 as entry from tablea UNION ALL
select d2 as entry from tableb UNION ALL
select d3 as entry from tablec UNION ALL
) as allTablesCombinedSubquery
order by count(*)
select TOP 1 entry
from #topThree
order by cnt desc
... it's extremely readable, and doesn't use any concepts that are tough to grok.

SQL get the closest two rows within duplicate rows

I have following table
ID Name Stage
1 A 1
1 B 2
1 C 3
1 A 4
1 N 5
1 B 6
1 J 7
1 C 8
1 D 9
1 E 10
I need output as below with parameters A and N need to select closest rows where difference between stage is smallest
ID Name Stage
1 A 4
1 N 5
I need to select rows where difference between stage is smallest
This query can make use of an index on (name, stage) efficiently:
WITH cte AS (
SELECT TOP 1
a.id AS a_id, a.name AS a_name, a.stage AS a_stage
, n.id AS n_id, n.name AS n_name, n.stage AS n_stage
FROM tbl a
CROSS APPLY (
SELECT TOP 1 *, stage - a.stage AS diff
FROM tbl
WHERE name = 'N'
AND stage >= a.stage
ORDER BY stage
UNION ALL
SELECT TOP 1 *, a.stage - stage AS diff
FROM tbl
WHERE name = 'N'
AND stage < a.stage
ORDER BY stage DESC
) n
WHERE a.name = 'A'
ORDER BY diff
)
SELECT a_id AS id, a_name AS name, a_stage AS stage FROM cte
UNION ALL
SELECT n_id, n_name, n_stage FROM cte;
SQL Server uses CROSS APPLY in place of standard-SQL LATERAL.
In case of ties (equal difference) the winner is arbitrary, unless you add more ORDER BY expressions as tiebreaker.
dbfiddle here
This solution works, if u know the minimum difference is always 1
SELECT *
FROM myTable as a
CROSS JOIN myTable as b
where a.stage-b.stage=1;
a.ID a.Name a.Stage b.ID b.Name b.Stage
1 A 4 1 N 5
Or simpler if u don't know the minimum
SELECT *
FROM myTable as a
CROSS JOIN myTable as b
where a.stage-b.stage in (SELECT min (a.stage-b.stage)
FROM myTable as a
CROSS JOIN myTable as b)

Postgresql query with left join and having

Postgresql 9.1: I have a query that must return the values of a second table only if the aggregate function SUM of two columns is greater than zero.
This is the data:
Table a
id
---
1
2
3
Table b
id fk(table a)
---------------
1 1
2 null
3 3
Table c
id fk(table b) amount price
-----------------------------------
1 1 1 10 --positive
2 1 1 -10 --negative
3 3 2 5
As you can see, table b has some ids from table a, and table c can have 1 or more references to table b, table c is candidate to be retrieved only if the sum(amount * price ) > 0.
I wrote this query:
SELECT
a.id, b.id, SUM(c.amount * c.price) amount
FROM
tablea a
LEFT JOIN
tableb b ON b.fk = a.id
LEFT JOIN
tablec c ON c.fk = b.id
GROUP BY
a.id, b.id
HAVING
SUM(c.amount * c.price) > 0
But this query is not retrieving all rows from table a just the row 1 and I need the two rows. I understand this is happening because of the HAVING clause but I don't know how to rewrite it.
Expected result
a b sum
------------------
1 null null -- the sum of 1 * 10 (rows 1 and two) = 0 so its not retrieved.
2 null null -- no foreign key in second table
3 3 10 -- the sum of 2 * 5 (row 3) > 0 so it's ok.
Try this:
SELECT A.ID, B.ID, C.ResultSum
FROM TableA A
LEFT JOIN TableB B ON (B.FK = A.ID)
LEFT JOIN (
SELECT FK, SUM(Amount * Price) AS ResultSum
FROM TableC
GROUP BY FK
) C ON (C.FK = B.ID) AND (ResultSum > 0)
See demo here.

access query to filter and combine count

i have two access tables
tableA
num count
1 7
2 8
3 9
4 9
5 13
6 6
tableB
num count
0 1
1 14
2 12
3 5
4 5
5 11
6 5
how can i create an access query that will ignore the numbers which have count less than 6 in any of the two tables. i.e. 0,3,4 & 6 and create a table with the rest of the numbers sorted by combined count
tableC
num count
5 24
1 21
2 20
any help appreciated
Maybe....
SELECT a.num, a.count + b.count
FROM tableA a
JOIN tableB b on b.num = a.num
WHERE a.count >= 6
AND b.count >= 6
this will include numbers which are in both A and B. To include numbers with count >= 6 that are in one table and not the other you'll have to add a Join and a "isnull" for the a.count and b.count values. ie; isnull(a.count,0) + isnull(b.count,0)
You can try something like this
SELECT DISTINCT tableA.num, [tableA].[val]+[tableB].[val] AS Expr1
FROM tableA INNER JOIN tableB ON tableA.num = tableB.num
WHERE (((tableA.val)>=6) AND ((tableB.val)>=6));
How about
SELECT x.Num, x.Count FROM (
SELECT Num, Count(*)
FROM tableA
GROUP BY Num
HAVING Count(*)>6
UNION ALL
SELECT Num, Count(*)
FROM tableB
GROUP BY Num
HAVING Count(*)>6) x
Or if count is a field, rather than a calculation:
SELECT x.Num, x.Count FROM (
SELECT Num, Count
FROM tableA
WHERE Count>6
UNION ALL
SELECT Num, Count
FROM tableB
WHERE Count>6) x