Update select using the max of one column Postgresql - sql

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))

Related

SQL query - select row basing on the joined table's criteria

I'm stuck within this simple scenario:
tableA
| ID | TableB_ID | Name |
tableA 1 ---> * tableB
tableB
| ID | Status_ID |
and I need to retrieve the Name column values from the tableA, whose contains rows in the tableB with Status_ID = 1 and Status_ID = 2 (two separate rows, it can be more with other values but it doesn't matter here)
Try this:
SELECT A.NAME
FROM TABLE_A AS A INNER JOIN TABLE_B AS B ON A.TABLEB_ID = B.ID
WHERE B.STATUS_ID IN (1, 2) -- OR OTHER VALUES

Simple SQL Questions with join

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

Stop returning multiple similar rows on joins

I have the following scenario in SQLite.
TableA
ID
-----
1 |
2 |
3 |
Table B
ID | AID |Tag
----------------
1 | 1 | Hide
2 | 1 | Show
3 | 2 | Null
4 | 3 | Show
Table B has column AID which is the IDs of table A.
In the example above
Table A ID: '1' has
-> Table B ID of '1' and '2' and Tags 'Hide' and 'Show' attached to it.
I am looking for an SQL that will only return, in the example above, Table A IDs: '2' and '3'.
Basically, as TableA ID: '1' has a 'Hide' tag attached to it, don't return it (even though it also has a show tag attached to it)
The SQL I am using is (excuses the names, this is just a quick example)
select
a.ID as a_ID,
b.ID as b_ID,
b.Tag as Tag
from
Table A as a
left join Table B on a.id = b.aID
and b.tag != 'hide'
The problem with this SQL it's still returning
a_ID | b_ID | Tag
-------------------------------
1 | 2 | Show
I'm a tad stuck and any help would be really appreciated. I'm not 100% sure how I would work this on for a google search.
The closest I got was this question How to return only 1 row if multiple duplicate rows and still return rows that are not duplicates?
but I couldn't work out how the GROUP BY would help here.
You're looking for an id where no row exists with a 'hide' tag. This is a direct translation into SQL:
select *
from TableA as a
where not exists
(
select *
from TableB as B
where a.id = b.aID
and b.tag = 'hide'
)
Or, if you want the data from TableB:
select *
from TableB as t1
where not exists
(
select *
from TableB as t2
where t1.aID = t2.aID
and t2.tag = 'hide'
)
You could use an extra condition with the exists operator:
SELECT a.ID AS a_ID, b.ID AS b_ID, b.Tag AS Tag
FROM table_a a
LEFT JOIN table_b b ON a.id = b.aID
WHERE NOT EXISTS (SELECT *
FROM table_b bin
WHERE bin.aID = a.ID AND bin.Tag = 'hide')

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

Select first record in a One-to-Many relation using left join

I'm trying to join two tables using a left-join. And the result set has to include only the first record from the "right" joined table.
Lets say I have two tables A and B as below;
Table "A"
code | emp_no
101 | 12222
102 | 23333
103 | 34444
104 | 45555
105 | 56666
Table "B"
code | city | county
101 | Glen Oaks | Queens
101 | Astoria | Queens
101 | Flushing | Queens
102 | Ridgewood | Brooklyn
103 | Bayside | New York
Expected Output:
code | emp_no | city | county
101 | 12222 | Glen Oaks | Queens
102 | 23333 | Ridgewood | Brooklyn
103 | 34444 | Bayside | New York
104 | 45555 | NULL | NULL
105 | 56666 | NULL | NULL
If you notice my result has only the one matched record from table "B"(doesn't matter what record is matched) after left join (and it is a one to many mapping)
I need to pick the first matched record from table B and ignore all other rows.
Please help!
Thanks
After playing around a bit, this turns out to be trickier than I'd expected! Assuming that table_b has some single column that is unique (say, a single-field primary key), it looks like you can do this:
SELECT table_a.code,
table_a.emp_no,
table_b.city,
table_b.county
FROM table_a
LEFT
JOIN table_b
ON table_b.code = table_a.code
AND table_b.field_that_is_unique =
( SELECT TOP 1
field_that_is_unique
FROM table_b
WHERE table_b.code = table_a.code
)
;
Another option: OUTER APPLY
If supported by the database, OUTER APPLY is an efficient and terse option.
SELECT *
FROM
Table_A a
OUTER APPLY
(SELECT TOP 1 *
FROM Table_B b_1
WHERE b_1.code = a.code
) b
;
This results in a left join to the indeterminate first matched record. My tests show it to be quicker than any other posted solution (on MS SQL Server 2012).
The highest voted answer does not seem correct to me, and seems overcomplicated.
Just group by the code field on table B in your subquery and select the maximum Id per grouping.
SELECT
table_a.code,
table_a.emp_no,
table_b.city,
table_b.county
FROM
table_a
LEFT JOIN
table_b
ON table_b.code = table_a.code
AND table_b.field_that_is_unique IN
(SELECT MAX(field_that_is_unique)
FROM table_b
GROUP BY table_b.code)
If you are on SQL Server 2005 or later version, you could use ranking to achieve what you want. In particular, ROW_NUMBER() seems to suit your needs nicely:
WITH B_ranked AS (
SELECT
*,
rnk = ROW_NUMBER() OVER (PARTITION BY code ORDER BY city)
FROM B
)
SELECT
A.code,
A.emp_no,
B.city,
B.county
FROM A
LEFT JOIN B_ranked AS B ON A.code = B.code AND b.rnk = 1
OR
WITH B_unique_code AS (
select * from(
SELECT
*,
rnk = ROW_NUMBER() OVER (PARTITION BY code ORDER BY city)
FROM B
) AS s
where rnk = 1
)
SELECT
A.code,
A.emp_no,
B.city,
B.county
FROM A
LEFT JOIN B_unique_code AS B ON A.code = B.code
I modified the answer from ruakh and this seem to work perfectly with mysql.
SELECT
table_a.code,
table_a.emp_no,
table_b.city,
table_b.county
FROM table_a a
LEFT JOIN table_b b
ON b.code = a.code
AND b.id = ( SELECT id FROM table_b
WHERE table_b.code = table_a.code
LIMIT 1
)
;
this is how:
Select * From TableA a
Left Join TableB b
On b.Code = a.Code
And [Here put criteria predicate that 'defines' what the first record is]
Hey, if the city and county are unique, then use them
Select * From TableA a
Left Join TableB b
On b.Code = a.Code
And b.City + b.county =
(Select Min(city + county)
From TableB
Where Code = b.Code)
But the point is you have to put some expression in there to tell the query processor what it means to be first.
In Oracle you can do:
WITH first_b AS (SELECT code, min(rowid) AS rid FROM b GROUP BY code))
SELECT a.code, a.emp_no, b.city, b.county
FROM a
INNER JOIN first_b
ON first_b.code = a.code
INNER JOIN b
ON b.rowid = first_b.rid