How to replace multiple fields values with values from another table? - sql

I have two tables: TableA
Id Status User
1 15 111
2 15 111
3 15 111
And TableB which contains history of status changes from previous table:
Id IdA Status Date
1 1 10 2023-01-18
2 1 30 2022-12-18
3 3 30 2022-01-17
4 3 10 2022-01-16
What I need to do is to update status field values for every row with User 111 in TableA with values from TableB, I need to find the latest entity change in TableB and write its status to the corresponding entity in TableA.
So the final result for the TableA should be:
Id Status User
1 10 111
2 15 111
3 30 111

update tableA
set status = latest_data.status
from (select distinct on (idA)
idA, status, date
from tableB
-- helps to retrieve the latest update for each unique idA
order by idA, date desc) latest_data
where latest_data.idA = tableA.id;
See the docs for DISTINCT ON clause.
See the demo with all the details.

Rank table b based on IdA ordered by date ascending then choose row number 1 to update table a.
Row_number()over(order by ...)
tableb is the second table and the tablen is the 1 table
update tablen t1 set status = t2.status
from
(select id, IDA, status from (
select *,
row_number ()over(partition by IDA order by daten desc) rnk
from tableb) x
where rnk = 1)t2 where t1.id = t2.idA;
this clause is to get the last update
select id, IDA, status from (
select *,
row_number ()over(partition by IDA order by daten desc) rnk
from tableb) x
where rnk = 1

Related

Update a column based on other rows column value

I have a table t which looks like this
key fill store end_date status
1 123 1 2019-04-30 0
2 1234 1 2019-04-30 0
3 123 1 2019-05-01 0
Now I need to update the first record and set status=1 as the third record has same fill, store value and it is latest.
Output:
key fill store end_date status
1 123 1 2019-04-30 1
2 1234 1 2019-04-30 0
3 123 1 2019-05-01 0
I tried calculating row_number and tried to update the column based on it but unable to figure out how to use the result in the update clause.
update t set
status = 1
from (
select *
from (
select *
, row_number() over (partition by fill, store order by end_dt desc) as row_num from t
) a
where row_num = 2
) b
This query is updating all the records, what should change in my query to get the expected result?
I think that you want:
with cte as (
select status, row_number() over(partition by fill, store order by end_date desc) rn
from t
)
update cte set status = 1 where rn > 1
In the common table expression, row_number() ranks records having the same fill and store by descending end_date. Then, the outer query sets status to 1 on rows that were not ranked first.
You can do a correlated subquery:
update my_table a
set status = 1
where exists (
select 1
from my_table b
where b.fill = a.fill
and b.store = a.store
and b.end_date > a.end_date
)

how to select a value based on unique id column

Please help me ,
I have table with 3 column , when i select the column i need to dulicate the value based on the id
Id Days Values
1 5 7
1 NULL NULL
1 NULL NULL
2 7 25
2 NULL NULL
2 8 274
2 NULL NULL
I need a Result as
Id Days Values
1 5 7
1 5 7
1 5 7
2 7 25
2 7 25
2 8 274
2 8 274
`
Generate a set of data with the desired repeating values (B). Then join back to the base set (A) containing the # of record to repeat. This assumes that each ID will only have one record populated. If this is not the case, then you will not get desired results.
SELECT B.ID, B.MDays as Days, B.Mvalues as values
FROM TABLE A
INNER JOIN (SELECT ID, max(days) mDays, Max(values) Mvalues
FROM Table
GROUP BY ID) B
on A.ID = B.ID
And due to updates in question....--
This will get you close but without a way to define grouping within ID's I can't subdivide the records into 2 and 2
SELECT B.ID, B.Days as Days, B.values as values
FROM TABLE A
INNER JOIN (SELECT Distinct ID, days, values
FROM Table
GROUP BY ID) B
on A.ID = B.ID
and A.days is null
This isn't even close enough as we still Don't know how to order the rows...
It assumes order within the table which can't be trusted. We generate a row number for each row in the table using the Row_number Over syntax Grouping (partition by) the ID and days with the order of ID days (which doesn't work because of the null values)
We then join this data set back to a distinct set of values on ID and days
to get us close... but we still need some grouping logic. beyond that of ID that handles the null records and lack of order or grouping.
With CTE AS (
SELECT ID, Days, Values, Row_Number() Over (partition by ID, Days ORDER BY ID, Days) RN
FROM Table)
SELECT *
FROM (SELECT Distinct ID, Days, Values, max(RN) mRN FROM CTE GROUP BY ID, Days, Values) A
INNER JOIN CTE B
ON A.ID = B.ID
and A.Days = B.Ddays
and mRN <= B.RN
Order by B.RN

Select row minimum col for each id

I have a table like following
id id_a id_b uds
--------------------------
1 1 3 20
1 2 8 17
2 1 3 5
3 1 1 32
3 2 1 6
What I would need is to get the row with minimum "uds" for each "id". So the result would be:
id id_a id_b uds
--------------------------
1 2 8 17
2 1 3 5
3 2 1 6
Thank you in advance...
Use Min with a group by clause:
select id, id_a, id_b, min(uds) as uds
from table1
group by id, id_a, id_b
order by id, id_a, id_b;
However, I should mention this is going to get you all of the items, you need to also specify an aggregate on the other columns, or do not include them.
select id, min(uds) as uds
from table1
group by id
order by id;
Judging by your desired output though, the following may be what you want:
select id, max(id_a) as id_a, max(id_b) as id_b, min(uds) as uds
from table1
group by id
order by id;
Most databases support the ANSI standard window functions. An easy way to do what you want:
select t.*
from (select t.*, row_number() over (partition by id order by uds) as seqnum
from t
) t
where seqnum = 1;
you have to set condition with minimum uds value or you have to decides how many numbers of records you want with minimum uds
More one way:
select
a.*
from
#temp a inner join (select id, min(uds) minUds from #temp group by id) b on
a.id = b.id
and a.uds = b.minUds

left join without duplicate values using MIN()

I have a table_1:
id custno
1 1
2 2
3 3
and a table_2:
id custno qty descr
1 1 10 a
2 1 7 b
3 2 4 c
4 3 7 d
5 1 5 e
6 1 5 f
When I run this query to show the minimum order quantities from every customer:
SELECT DISTINCT table_1.custno,table_2.qty,table_2.descr
FROM table_1
LEFT OUTER JOIN table_2
ON table_1.custno = table_2.custno AND qty = (SELECT MIN(qty) FROM table_2
WHERE table_2.custno = table_1.custno )
Then I get this result:
custno qty descr
1 5 e
1 5 f
2 4 c
3 7 d
Customer 1 appears twice each time with the same minimum qty (& a different description) but I only want to see customer 1 appear once. I don't care if that is the record with 'e' as a description or 'f' as a description.
First of all... I'm not sure why you need to include table_1 in the queries to begin with:
select custno, min(qty) as min_qty
from table_2
group by custno;
But just in case there is other information that you need that wasn't included in the question:
select table_1.custno, ifnull(min(qty),0) as min_qty
from table_1
left outer join table_2
on table_1.custno = table_2.custno
group by table_1.custno;
"Generic" SQL way:
SELECT table_1.custno,table_2.qty,table_2.descr
FROM table_1, table_2
WHERE table_2.id = (SELECT TOP 1 id
FROM table_2
WHERE custno = table_1.custno
ORDER BY qty )
SQL 2008 way (probably faster):
SELECT custno, qty, descr
FROM
(SELECT
custno,
qty,
descr,
ROW_NUMBER() OVER (PARTITION BY custno ORDER BY qty) RowNum
FROM table_2
) A
WHERE RowNum = 1
If you use SQL-Server you could use ROW_NUMBER and a CTE:
WITH CTE AS
(
SELECT table_1.custno,table_2.qty,table_2.descr,
RN = ROW_NUMBER() OVER ( PARTITION BY table_1.custno
Order By table_2.qty ASC)
FROM table_1
LEFT OUTER JOIN table_2
ON table_1.custno = table_2.custno
)
SELECT custno, qty,descr
FROM CTE
WHERE RN = 1
Demolink

How to substract rows from one table from another only once

I'm working for a university project, and I have the following question:
I have 2 tables in a Oracle DB... I need to select those rows from table1, which are not included in table2... But the main problem is that I need to exclude that rows from table2 wich was selected once... For example:
Table1 Table2 ResultTable
id | Number | Letter id | Number | Letter id | Number | Letter
_____________________ _____________________ _____________________
1 4 S 1 6 G 2 2 P
2 2 P 2 8 B 3 5 B
3 5 B 3 4 S 4 4 S
4 4 S 4 1 A 6 2 P
5 1 A 5 1 H
6 2 P 6 2 X
So, how you see it, if one row from Table1 has a "twin" in Table2, they both are excluded.
Probably the most thorough query is this:
SELECT table1.id,
table1.digit,
table1.letter
FROM ( SELECT id,
digit,
letter,
ROW_NUMBER() OVER (PARTITION BY digit, letter ORDER BY id) rn
FROM table1
) table1
LEFT
JOIN ( SELECT id,
digit,
letter,
ROW_NUMBER() OVER (PARTITION BY digit, letter ORDER BY id) rn
FROM table2
) table2
ON table2.digit = table1.digit
AND table2.letter = table1.letter
AND table2.rn = table1.rn
WHERE table2.id IS NULL
ORDER
BY table1.id
;
which gives each record in table1 and table2 a "row number" within its group of "twins". For example, this:
SELECT id,
digit,
letter,
ROW_NUMBER() OVER (PARTITION BY digit, letter ORDER BY id) rn
FROM table1
ORDER
BY table1.id
;
returns this:
ID DIGIT LETT RN
---------- ---------- ---- ----------
1 4 S 1
2 2 P 1
3 5 B 1
4 4 S 2 -- second row with 4 S
5 1 A 1
6 2 P 2 -- second row with 2 P
That said, if you know that no (digit, letter) can ever appear more than once in table2, you can simplify this considerably by using EXISTS instead of ROW_NUMBER():
SELECT id,
digit,
letter
FROM table1 table1a
WHERE EXISTS
( SELECT 1
FROM table1
WHERE digit = table1a.digit
AND letter = table1a.letter
AND id < table1a.id
)
OR NOT EXISTS
( SELECT 1
FROM table2
WHERE digit = table1a.digit
AND letter = table1a.letter
)
;
Break it into parts.
Perhaps you have an EOR - Exclusive OR.
So you might have
(condition1
OR
condition2)
AND NOT
(condition1 AND condition2).
Use the Oracle MINUS keyword, which does exactly what you're asking for. See http://oreilly.com/catalog/mastorasql/chapter/ch07.html for more detail.
I can't see how to do what you want with one SQL SELECT.
I think you'll need a temporary table, and several statements.
Call it tmpResults, with id1 and id2, which match the id from Table1 and the id from Table2 respectively.
-- get matched rows - this is too many, we'll delete some later.
INSERT INTO tmpResults (id1, id2)
SELECT Table1.id id1, Table2.id id2
FROM Table1 INNER JOIN Table2
ON Table1.Number = Table2.Number AND Table1.Letter = Table2.Letter;
-- Delete where Table1 has matched more than 1 row
DELETE tmpResults
WHERE rowid IN
(SELECT tmpResults.RowId
FROM tmpResults
INNER JOIN
(SELECT id1, MAX(id2) id2m FROM tmpResults GROUP BY id1 HAVING count(*) > 1) m1
ON tmpResults.id1 = m1.id1 AND tmpResults.id2 = m1.id2m );
-- Delete where Table2 has matched more than 1 row
DELETE tmpResults
WHERE rowid IN
(SELECT tmpResults.RowId
FROM tmpResults
INNER JOIN
(SELECT MAX(id1) id1m, id2 FROM tmpResults GROUP BY id2 HAVING count(*) >1) m2
ON tmpResults.id1 = m2.id1m AND tmpResults.id2 = m2.id2 );
-- now tmpResults should have unique matches only, so we want Table1 where there is no match
SELECT Table1.*
FROM Table1
LEFT JOIN tmpResults
ON table1.id = tmpResults.id1
WHERE tmpResults.id2 IS NULL;