Simple SQL Questions with join - sql

i have some problems with join tables:
Table A -> ID,Col1,Col2,Col3
Table B -> Rank , ColX , A_ID (Relationship with A.ID)
I want to take higher Rank (each A_ID , like group by A_ID) of B table
my results must be something like A.ID , Col1 , Col2 , Col3 , ""ColX"" , how can i do that ?
and i want my result count equals to A.ID count.
TableA
+--------------------+
| ID|Col1|Col2|Col3| |
+--------------------+
| 1 | C1 | C2 | C3 |
| 2 | C1 | C2 | C3 |
+--------------------+
TABLE_B
+-----------------------------+
| ID| COL_X |RANK |A_ID| |
+-----------------------------+
| 1 | SomeValue | 1 | 1 |
| 2 | some22222 | 2 | 1 |
| 3 | SOMEXXXX | 3 | 1 |
| 4 | SOMEVAL | 1 | 2 |
| 5 | VALUE | 2 | 2 |
+-----------------------------+
Expected Output:
+--------------------------------------------------------------------+
| ID| Col1| Col2 | Col3| COLX |
+--------------------------------------------------------------------+
| 1 | C1 | C2 | C3 | SOMEXXXX (Higher Rank of TableB-> A_ID = 1) |
| 2 | C1 | C2 | C3 | VALUE (Higher Rank of TableB-> A_ID = 2) |
+--------------------------------------------------------------------+

You could easily do this using a subquery by first finding the max for each A_ID and then joining to tableA and TableB to get your desired rows:
SELECT a.ID,
a.col1,
a.Col2,
a.Col3,
b1.Col_X
FROM (
SELECT a_id
,max(rank) AS MaxRank
FROM tableb
GROUP BY a_id
) b
INNER JOIN tablea a ON a.id = b.a_id
INNER JOIN tableb b1 ON b.a_id = b1.a_id AND b1.rank = b.MaxRank
ORDER BY a.ID;
SQL Fiddle Demo

I'm thinking you want to take the max rank from your table b for each row in table a?
There's lots of different ways of approaching this. Here's one simple one:
with maxCTE as
(select
a_id,
max(rank) as MaxRank
from
tableb
group by
a_id
)
select
*
from
tablea a
inner join tableb b
on a.id = b.a_id
inner join maxcte c
on b.a_id = c.a_id
and b.rank = c.MaxRank
SQLFiddle
Basically, the CTE identifies the max rank for each a_id, then we join that back to tableb to get the details about that row.

with x as
(select a_id, max(rank) as mxrnk
from tableB
group by a_id)
select a.id, a.col1, a.col2, a.col3, b.col_x
from tableA a join x
on a.id = x.a_id
join tableB b
on x.mxrnk = b.rank
You can select max rank per a_id in the cte and use it to select the corresponding columns.

One is to INNER JOIN Table B onto Table A by ID's. You will have 3 records returned from Table B. If you ORDER those records by the COLX
SELECT
,a.ID
,a.Col1
,a.Col2
,a.Col3
,b.COLX
FROM TableA AS a
INNER JOIN TABLE_B AS b on b.A_ID = a.id
ORDER BY b.COLX DESC
Then another way is joining a sub query of Table B that also has a sub query that filters Table B records to only the records with the highest RANK.
That way you can bring in COLX values from the highest RANK records from Table B that match the records of Table A.
I think at least...
SELECT
a.ID
,a.Col1
,a.Col2
,a.Col3
,b.COLX
FROM TableA a
INNER JOIN (
SELECT
a.A_ID
,a.RANK
,a.COLX
FROM TABLE_B a
INNER JOIN (
SELECT
A_ID,
,MAX(RANK) AS [RANK] -- Highest Rank
FROM TABLE_B
GROUP BY A_ID
) AS b ON b.A_ID = a.A_ID AND b.RANK = a.RANK
) AS b on b.A_ID = a.id
ORDER BY a.ID ASC

Select A.*,D.Col_X
from
(Select C.COL_X,B.A_ID
from
(Select A_ID,MAX(rank) as MAX_rank
from TABLE_B
group by A_ID) B ----- gets the highest rank and ID of the highest rank
inner join TABLE_B c
on
concat(C.A_ID,C.RANK)= concat(B.A_ID,B.MAX_rank)) D ---- Gets the highest rank name
inner join TABLE_A A
on D.A_ID=A.ID
OUTPUT:
ID Col1 Col2 Col3 Col_X
1 c1 c2 c3 SOMEXXXX
2 c1 c2 c3 VALUE

Related

JOIN - Returns 5 instead of 3 rows for repeating values in ID column

I have the following two tables:
Table A:
id|valA
--+----
1 | a
2 | b
2 | c
Table B:
id|valA
--+----
1 | A
2 | B
2 | C
What I expect to get using join is:
id|valA|valB
--+----+----
1 | a | A
2 | b | B
2 | c | C
However, I get the following
id|valA|valB
--+----+----
1 | a | A
2 | b | B
2 | c | B
2 | c | C
2 | b | C
I am using the following query:
select id, valA, valB from A
inner join B
on A.id = B.id;
How do I use the join to get the expected results?
What am I doing wrong?
Both tables have a duplicate ID of 2, so you get a result with all the possible combinations of the two rows in each table with that ID (bB, bC, cB and cC). If you can rely on the values, you can add them to the join condition:
SELECT a.id, valA, valB
FROM a
JOIN b ON a.id = a.id and valA = LOWER(valB)
If you can't make this assumption, you could add a rank pseudocolumn to the values with the same ID and use in in the join condition too, although it may be a bit clunky:
SELECT idA, valA, valB
FROM (SELECT id AS idA, valA, RANK() OVER (PARTITION BY id ORDER BY valA) AS rankA
FROM a) a
JOIN (SELECT id AS idB, valB, RANK() OVER (PARTITION BY id ORDER BY valB) AS rankB
FROM b) b ON idA = idB AND rankA = rankB

Update select using the max of one column Postgresql

So I'm changing a db structure. I'm moving a column, 'size', from table_B to table_A.
Table_B can have multiple entries referencing table_A.a_id
I want to update the new 'size' column in table_A from table_B with the row with the highest b_id.
So I can use Max() and group by to select just the highest b_ids for each a_id.
SELECT max(b_id)
FROM table_B
GROUP BY a_id;
But I'm not sure how to fit that in with the update select
UPDATE table_A
SET table_A.size = table_B.size
FROM table_A, table_B
WHERE table_A.a_id = table_B.a_id
AND table_B.b_id =
(
max...
);
edit:
I realise now this actually isn't an update select, my first attempt was
How table_B looks and the expected result in table_A
table_A
a_id | size
-------------------------
1 | 5678
2 | 456
table_B
b_id | a_id | size
--------------------------------------
1 | 1 | 1234
2 | 1 | 5678
3 | 2 | 456
First, write a select to get the size you want:
select distinct on (b.a_id) b.a_id, b.size
from tableb b
order by b.a_id, b.b_id desc;
Next, incorporate this into the update:
update tablea a
set size = b.size
from (select distinct on (b.a_id) b.*
from tableb b
order by b.a_id, b.b_id desc
) b
where a.a_id = b.a_id;
you can try the following query
UPDATE table_A
SET table_A.size =
( SELECT table_B.size
FROM table_B B1
WHERE table_A.a_id = B1.a_id AND
B1.b_id = (SELECT max(b_id) FROM table_B B2 WHERE table_A.a_id=B2.a_id))

SQL filter rows based without using Group by

I have a query which will perform joins over 6 tables and fetches various columns based on a condition. I want to add an extra filter condition which will give me only those members who have a count(distinct dateCaptured)>30. I'm able to get the list of members who satisfy this condition using Group by and having. But I don't want to group by other column names because of this one condition. Do I need to use PARTITION BY in this case.
Sample TABLE a
+-----+------------+--------------+
| Id | Identifier | DateCaptured |
+-----+------------+--------------+
| 1 | 05548 | 2017-09-01 |
| 2 | 05548 | 2017-09-01 |
| 3 | 05548 | 2017-09-01 |
| 4 | 05548 | 2017-09-02 |
| 5 | 05548 | 2017-09-03 |
| 6 | 05548 | 2017-09-04 |
| 7 | 37348 | 2017-08-15 |
| 8 | 37348 | 2017-08-15 |
| . | | |
| . | | |
| . | | |
| 54 | 37348 | 2017-10-15 |
+-----+------------+--------------+
Query
SELECT a.value,
b.value, c.value,
d.value
FROM Table a
INNER JOIN Table b on a.Id=b.id
INNER JOIN Table c on a.Id=c.Id and s.Invalid=0
INNER JOIN Table d on a.Id=d.Id
Assume Table a has more than 30 records for Identifier 37348. How can I get only this Identifier for the above query.
These are the patients i'm interested in for the above SELECT.
SELECT a.Identifier,count(DISTINCT DateCaptured)
FROM Table a
INNER JOIN Table b on a.Id=b.id
INNER JOIN Table c on a.Id=c.Id and s.Invalid=0
INNER JOIN Table d on a.Id=d.Id
GROUP BY Identifier
HAVING count(DISTINCT DateCaptured)>30
WITH cte as (
SELECT a.Identifier
FROM Table a
INNER JOIN Table b on a.Id=b.id
INNER JOIN Table c on a.Id=c.Id and s.Invalid=0
INNER JOIN Table d on a.Id=d.Id
GROUP BY Identifier
HAVING count(DISTINCT DateCaptured) > 30
)
SELECT a.value,
b.value, c.value,
d.value
FROM Table a
INNER JOIN Table b on a.Id=b.id
INNER JOIN Table c on a.Id=c.Id and s.Invalid=0
INNER JOIN Table d on a.Id=d.Id
INNER JOIN cte on cte.Identifier = a.Identifier
SELECT a.value,
b.value, c.value,
d.value
FROM Table a
INNER JOIN Table b on a.Id=b.id
INNER JOIN Table c on a.Id=c.Id and s.Invalid=0
INNER JOIN Table d on a.Id=d.Id
WHERE a.Identifier IN (SELECT a1.Identifier
FROM Table a1
GROUP BY a1.Identifier HAVING count(DISTINCT a1.DateCaptured)>30)
If the multiple rows really are in tableA, then you can do:
SELECT a.value, b.value, c.value, d.value
FROM (SELECT a.*, COUNT(*) OVER (PARTITION BY id) as cnt
FROM a
) a INNER JOIN
b
ON a.Id = b.id INNER JOIN
c
ON a.Id = c.Id AND s.Invalid = 0 INNER JOIN
d
ON a.Id = d.Id
WHERE a.cnt > 30;
Note: If you still need count(distinct) you can do:
SELECT a.value, b.value, c.value, d.value
FROM (SELECT a.*, SUM(CASE WHEN seqnum = 1 THEN 1 ELSE 0 END) OVER (PARTITION BY id) as cnt
FROM (SELECT a.*, ROW_NUMBER() OVER (PARTITION BY id ORDER BY DateCaptured) as seqnum
FROM a
) a
) a INNER JOIN
b
ON a.Id = b.id INNER JOIN
c
ON a.Id = c.Id AND s.Invalid = 0 INNER JOIN
d
ON a.Id = d.Id
WHERE a.cnt > 30;

TSQL - retrieve results from table A that contains exact data contained in table B

I have TableA (id bigint, name varchar) and TableB (name varchar) that contains the following data:
Table A: Table B: Results:
------------- --------- ---------------
| 1 | "A" | | "A" | | 1 | "A" |
| 1 | "B" | | "B" | | 1 | "B" |
| 2 | "A" | --------- | 4 | "A" |
| 3 | "B" | | 4 | "B" |
| 4 | "A" | ---------------
| 4 | "B" |
-------------
I want to return results from TableA that contains an EXACT match of what's in table B.
Using the 'IN' clause only retrieves back an occurrence.
Also, another example, if TableB has only "A", I want it to return back: 2-"A"
I understand your question but it is a tricky one as not exactly in line with the relational logic. You are looking for id's for which SELECT name FROM TableA WHERE id IN ... ORDER BY name; is identical to SELECT name FROM B order by name;.
Can you assume that A(id,name) is unique and B(name) is unique? Better said, are there constraints like that or can you set them up?
If yes, here is a solution:
1. Get rid of ids in A with rows not matching the rows in B
SELECT id, A.name FROM A WHERE id NOT IN
(SELECT id FROM A LEFT JOIN B ON A.name = B.name WHERE B.name IS NULL);
2. Count rows per each id (this is why the unique constraints are necessary)
SELECT id, COUNT(*) FROM
(
SELECT id, A.name FROM A WHERE id NOT IN
(SELECT id FROM A LEFT JOIN B ON A.name = B.name WHERE B.name IS NULL)
) t
GROUP BY id;
3. Only retain those that match the number of rows of B.
SELECT id, COUNT(*) FROM
(
SELECT id, A.name FROM A WHERE id NOT IN
(SELECT id FROM A LEFT JOIN B ON A.name = B.name WHERE B.name IS NULL)
) t
GROUP BY id
HAVING COUNT(*) = SELECT COUNT(*) FROM B;
This works in SQL Server
select * from TableA a
where
(select count(*) from TableB) = (select count(*) from TableA where id = a.id) and
(select count(*) from TableB) =
(
select count(*) from
(
select name from TableA where id = a.id
intersect
select name from TableB
) as b
)

SQL Join to Get Row with Maximum Value from Right table

I am having problem with sql join (oracle/ms sql)
I have two tables
A
ID | B_ID
---|------
1 | 1
1 | 4
2 | 3
2 | 2
----------
B
B_ID | B_VA| B_VB
-------|--------|-------
1 | 1 | a
2 | 2 | b
3 | 5 | c
4 | 2 | d
-----------------------
From these two tables I need A.ID, B.B_ID, B.B_VA (MAX), B.B_VB (with max B.B_VA)
So result table would be like
ID | B_ID | B_VA| B_VB
-------|--------|--------|-------
1 | 4 | 2 | d
2 | 3 | 5 | c
I tried some joins without success. Can anyone help me with query to get the result I want.
Thank you
Your logic as described doesn't quite correspond to the data. For instance, b_va is numeric, but the column in the output is a string.
Perhaps you want this. The data in a to be aggregated to get the maximum b_id value. Then each column to be joined to get the corresponding b_vb column. That, at least, conforms to your desired output:
select a.id, a.b_id, b1.b_vb as b_va, b2.b_vb
from (select id, max(b_id) as b_id
from a
group by id
) a join
b b1
on a.id = b1.b_id join
b b2
on a.b_id = b2.b_id;
EDIT:
For the corrected data, I think this is what you want:
select a.id, a.b_id, max(b1.b_va) as b_va, b2.b_vb
from (select id, max(b_id) as b_id
from a
group by id
) a join
b b1
on a.id = b1.b_id join
b b2
on a.b_id = b2.b_id
group by a.id, a.b_id, b2.b_vb;
Try this
SELECT X.ID, Y.B_ID, X.B_VA, Y.B_VB
FROM (SELECT A.ID, MAX(B_VA) AS B_VA
FROM A INNER JOIN B ON A.B_ID = B.B_ID
GROUP BY A.ID) AS X INNER JOIN
A AS Z ON X.ID = Z.ID INNER JOIN
B AS Y ON Z.B_ID=Y.B_ID AND X.B_VA=Y.B_VA