sql two where clauses from one table into two new rows - sql

I have the following tableA:
column A | column B | column C
-------------------------------------
1 |10 |a
2 |10 |b
3 |10 |c
4 |10 |d
5 |20 |a
6 |20 |b
7 |20 |c
8 |20 |d
9 |30 |a
10 |30 |b
11 |30 |c
12 |30 |d
I want to get this output:
column A-10 | column A-20 | column C
-------------------------------------
1 |5 |a
2 |6 |b
3 |7 |c
4 |8 |d
I tried this SQL query:
From tableA select column A, column C WHERE column B = 10
Which works and gives me the expected results. However if I add a second WHERE clauses
From tableA select column A, column C WHERE column B = 10 AND column B = 20
I get an empty return. How to the correct output?

If I understand correctly, you can use aggregation and window functions:
select max(case when b = 10 then a end) as a_10,
max(case when b = 20 then a end) as a_20,
c
from (select t.*,
row_number() over (partition by c, b order by a) as seqnum
from t
) t
group by c, seqnum;

You should use join operator here.
select
t1.A as a10
,t2.A as a20
,t1.C as c
from (
select column A, column C From tableA WHERE column B = 10
) t1
inner join (
select column A, column C From tableA WHERE column B = 20
) t2
on t1.C = t2.C

You can't have both 10 AND 20 which is why it is blank when it is run.
You can however have 10 OR 20.
Swap out AND for OR and it should get you the result you're looking for.

Correct query:
select tab1.A as a10 ,tab2.A as a20 ,tab1.C as c
from (
select column A, column C From tableA WHERE column B = 10
) tab1,
(
select column A, column C From tableA WHERE column B = 20
) tab2
where tab.C = tab.C

Related

Get correct records based on column from union which are used in join

This select is used as left outer join in package:
SELECT * (SELECT db1.id, db2.value, db1.discount, 2 AS attr_number FROM database1 db1
JOIN database2 db2 ON db2.db1_id = db1.id
WHERE db2.value = 1
UNION
SELECT db1.id, db4.value, db1.discount, 1 AS attr_number FROM database1 db1
JOIN database4 db4 ON db4.db1_id = db1.id
WHERE db4.value = 1) WHERE id = 225
He returns me this records:
|ID |VALUE |DISCOUNT |ATTR_NUMBER |
|-------------|------------------|------------------|------------------|
|225 |1 |50 |2 |
|225 |1 |50 |2 |
|225 |1 |40 |1 |
|225 |1 |40 |1 |
|225 |1 |40 |1 |
So i need to take attr_number row in consideration and fetch records based on that field.
As you can see, the attr_number row value could be only 1 or 2.
Per this example, records exists with both values, in this case we need to return only where attr_number = 1(because it exists), so this is an example what he should return:
|ID |VALUE |DISCOUNT |ATTR_NUMBER |
|-------------|------------------|------------------|------------------|
|225 |1 |40 |1 |
|225 |1 |40 |1 |
|225 |1 |40 |1 |
As you can see, he "removed" records where attr_number = 2 and returning only where it's 1.
In other case, if select do not return records where attr_number = 1, he returns all other records, in this situation it would be where attr_number = 2. This is an example what he should return in this case:
|ID |VALUE |DISCOUNT |ATTR_NUMBER |
|-------------|------------------|------------------|------------------|
|225 |1 |50 |2 |
|225 |1 |50 |2 |
Hope my explanation is clear enough.
You can use RANK to rank your results and only keep the best rows (i.e. those with the lower attr_number).
select db1.id, 1 as value, db1.discount, dbx.attr_number
from database1 db1
join
(
select db1_id, attr_number, rank() over (order by attr_number) as rn
from
(
select db1_id, 2 as attr_number from database2 db2 where value = 1
union all
select db1_id, 1 as attr_number from database4 db4 where value = 1
)
) dbx on dbx.db1_id = db1.id and dbx.rn = 1
where db1.id = 225;
Create 2 CTEs, one for each of your unioned queries and then use NOT EXISTS:
WITH
cte1 AS (
SELECT db1.id, db4.value, db1.discount, 1 AS attr_number
FROM database1 db1 JOIN database4 db4
ON db4.db1_id = db1.id
WHERE id = 225 AND value = 1
),
cte2 AS (
SELECT db1.id, db2.value, db1.discount, 2 AS attr_number
FROM database1 db1 JOIN database2 db2
ON db2.db1_id = db1.id
WHERE id = 225 db2.value = 1
)
SELECT * FROM cte1
UNION ALL
SELECT * FROM cte2
WHERE NOT EXISTS (SELECT 1 FROM cte1 WHERE attr_number = 1)

Select two entry from a joined table (1:m)

I have a device table (dev) and device_date table (dev_data). Relationship 1:M
dev table:
| id |name |status |
|-----|-------|-------|
| 1 |a |111 |
|-----|-------|-------|
| 2 |b |123 |
|-----|-------|-------|
| ....|..... |.... |
dev_data table:
|id |dev_id |status |date |
|---|-------|--------|------------------------|
|1 |1 | 123 |2019-04-16T18:53:07.908Z|
|---|-------|--------|------------------------|
|2 |1 | 120 |2019-04-16T18:54:07.908Z|
|---|-------|--------|------------------------|
|3 |1 | 1207 |2019-04-16T18:55:07.908Z|
|---|-------|--------|------------------------|
|4 |2 | 123 |2019-04-16T18:53:08.908Z|
|---|-------|--------|------------------------|
|5 |2 | 121 |2019-04-16T18:54:08.908Z|
|---|-------|--------|------------------------|
|6 |2 | 127 |2019-04-16T18:55:08.908Z|
|...|.......|........|........................|
I need to select all dev and join dev_data, but add only 2 last records (by date)
the final response should look like this one:
status_calc_1 and status_calc_2 is diff between status in dev and dev_data
status_calc_1 => status difference of the last row from dev_data and dev
status_calc_2 => status difference of prelast row from dev_data and dev
|id |name |status_calc_1 | status_calc_2 |
|----|------|---------------|---------------|
|1 |a |1207-111 |120-111 |
|----|------|---------------|---------------|
|2 |b |127-123 |121-123 |
I tried this one:
select id, "name", status, max(dd.date) as last,
(select date from device_data p where p.dev_id = device.id and date < dd.date limit 1) as prelast
from device
inner join device_data dd on device.id = dd.dev_id
group by id, "name", status;
but get an error:
ERROR: subquery uses ungrouped column "device.id" from outer query
and this one:
select id, "name", status, max(dd.date) as last, max(dd2.date) as prelast,
from device
inner join device_data dd on device.id = dd.dev_id
inner join device_data dd2 on device.id = dd2.dev_id and dd2.date < dd.date
group by id, "name", status;
I get correct 2 last dev_data, but still, have no idea how to make 2 columns status_calc_1 and status_calc_2
status_calc_1 = last row dev_data.status - dev.status
status_calc_2 = prelast row dev_data.status - dev.status
You can use conditional aggregation:
select d.id, d.name, d.status,
max(dd.date) as last,
max(case when dd.seqnum = 2 then dd.date end) as prelast,
(max(case when dd.seqnum = 1 then dd.status end) - d.status) as status_calc_1,
(max(case when dd.seqnum = 2 then dd.status end) - d.status) as status_calc_2
from device d join
(select dd.*,
row_number() over (partition by dd.dev_id order by dd.date desc) as seqnum
from device_data dd
) dd
on d.id = dd.dev_id
where seqnum <= 2
group by d.id, d.name, d.status;

How to make MS SQL select on this

I have MS SQL database table like this
TableA
+----+-----------+--------+
|ID | Table2_FK | Value |
+----+-----------+--------+
|1 | 7 | X |
|2 | 7 | Y |
|3 | 8 | X |
|4 | 8 | Z |
|5 | 9 | W |
|6 | 9 | M |
|5 | 10 | X |
|6 | 10 | Z |
+----+-----------+--------+
I want to make query to get list of Table2_FKs if I pass X and Z in query for Values. In this example 8 and 10 is the result
It can be more than 2 values
You can do this with group by and having:
select table2_fk
from t
where value in ('X', 'Z')
group by table2_fk
having count(*) = 2;
If the values can be duplicated for a key value, then use count(distinct value) = 2. The "2" is the number of values in the IN list.
Try this:
select distinct Table2_FK
from TableA
where value in ('X','Z');
You can use query as below:
Select distinct table2_fk from (
Select *, Ct = count(id) over (partition by table2_fk) from yourtable
) a
Where a.[Value] in ('X','Z') and a.Ct >= 2
you can use a query like below
select
distinct Table2_FK
from TableA a
where exists (
select 1 1 from TableA b where b.value ='X' and a.Table2_FK =b.Table2_FK
)
and exists (
select 1 1 from TableA c where c.value ='Z' and a.Table2_FK =c.Table2_FK
)

On MS Access, group by then filter

I have a table on ms access which has 13 columns.I want to group by column Name then check the latest one by comparing column id and take the record if the latest row has value if not take the previous record. the comparison will be done for each columns.
+-----+-----+-------+-------+-------+
| id |Name |colum1 |colum2 |colum3 |
+-----+-----+-------+-------+-------+
| 1 |a |x | |x |
+-----+-----+-------+-------+-------+
| 2 |b | |y |y |
+-----+-----+-------+-------+-------+
| 3 |a |z |z | |
+-----+-----+-------+-------+-------+
| 4 |a |m | | |
+-----+-----+-------+-------+-------+
Expected output
+-----+-----+-------+-------+-------+
| id |Name |colum1 |colum2 |colum3 |
+-----+-----+-------+-------+-------+
| 2 |b | |y |y |
+-----+-----+-------+-------+-------+
| 4 |a |m |z |x |
+-----+-----+-------+-------+-------+
You can do Self Join.
SELECT T1.*
FROM
table_name T1
INNER JOIN
(SELECT `Name`,MAX(`id`) AS ID FROM table_name GROUP BY `Name` ) T2
ON T1.`id`= T2.`ID` AND T1.`Name` = T2.`Name`
Hope this helps.
I'm not sure if it will work in MS Access. It works in SQL Server. Even if it does, it will be very slow.
SELECT
Groups.Name
,(
SELECT TOP(1) T.colum1
FROM T
WHERE T.Name = Groups.Name AND T.colum1 <> ''
ORDER BY T.ID DESC
) AS C1
,(
SELECT TOP(1) T.colum2
FROM T
WHERE T.Name = Groups.Name AND T.colum2 <> ''
ORDER BY T.ID DESC
) AS C2
,(
SELECT TOP(1) T.colum3
FROM T
WHERE T.Name = Groups.Name AND T.colum3 <> ''
ORDER BY T.ID DESC
) AS C3
FROM
(
SELECT Name
FROM T
GROUP BY Name
) AS Groups

Equivalent query using joins, instead of not exists

I'm trying to figure out how to get the same results using joins, as I would using a not exists condition.
For example, if I had the following two tables:
TABLE 1
--------------
|ID | EXT_ID |
|1 | A |
|2 | B |
|3 | C |
--------------
TABLE 2
-------------------------
|EXT_ID | TB1_ID |PRIMARY|
|A | 1 |1 |
|A | 1 |0 |
|B | 2 |0 |
|B | 2 |0 |
-------------------------
If I'm looking to find the records from table 1 that do not have a primary flag of 1 in table 2, for records that actually have a child in table 2 (to exclude orphans), I could simply write the following (expected to return only ID 2 from table 1):
SELECT TB1.ID FROM Table1 TB1
JOIN Table2
ON Table1.EXT_ID = Table2.EXT_ID
WHERE Table2.Primary = 0
AND NOT EXISTS
(
SELECT * FROM Table2 TB2
WHERE TB1.ID = TB2.TB1_ID
AND TB2.PRIMARY = 1
)
Is there a way to do this with joins? And if so, would there be much efficiency in using the not exists vs. a join?
Thanks in advance!
EDIT: fixed tables.
with x as (select Ext_ID, Tb1_ID, Sum(Primary) as sum_primary
from Table2 group by Ext_ID,Tb1_ID)
SELECT TB1.ID
FROM Table1 TB1 JOIN x
ON Table1.EXT_ID = x.EXT_ID
where x.sum_primary = 0
You can use a CTE for this.
SELECT
TB1.ID, TB1.EXT_ID
FROM
TABLE1 TB1
JOIN TABLE2 TB2 ON TB1.ID = TB2.TB1_ID AND TB1.EXT_ID = Table2.EXT_ID
GROUP BY
TB1.ID, TB1.EXT_ID
HAVING MAX(TB2.[PRIMARY]) = 0