Oracle - update data in first table with rows from second table - sql

How to update 'date_from' (t1) using 'modfied' (t2) when it is like 20/07/20.
So in this case in t1 id's 1 and 2 are to be updated and id 3 stays.
Table 1:
id date_from
-----------------------
1 13/07/30
2 13/07/30
3 13/07/30
Table 2:
id name modified
-----------------------
1 x 20/07/20
2 y 20/07/20
3 z 19/05/10

Something like this:
update t1 a set
a.date_from = (select b.modified
from t2 b
where b.id = a.id
and b.modified = date '2020-07-20'
)
where exists (select null
from t2 c
where c.id = a.id
and c.modified = date '2020-07-20'
)

If speed matters then,
merge into t1 trg
using
(
select id, modified
from t2
where modified = date'2020-07-20'
) src
on ( trg.id = src.id )
when matched then update
set trg.date_from = src.modified
where lnnvl(trg.date_from = src.modified);

You know in advance which value needs to be assigned, so you just need to filter which rows should be updated. exists seems sufficient:
update t1
set date_from = date '2020-07-20'
where exists (
select 1 from t2 where t2.id = t1.id and t2.modified = date '2020-07-20'
)

Related

How to select groups which has only the values we want and not select it if it also has other values in SQL

id
code
value
A
cod
2
A
buy
34
A
cod
4
B
cod
44
B
F
23
C
thk
45
C
cod
33
C
F
31
D
cod
22
In this table for example, I want those groups of id which has 'code' column value as ONLY cod or F. so query should return values of id = B and nothing else. ( Not even values with id = C because id=C also has 'thk' in code , not even id= D, and output should have ids with ONLY the mentioned two values)
expected output
id
code
value
B
cod
44
B
F
23
You want all rows for the ID of which not exists a forbidden row:
select id, code, value
from mytable
where not exists
(
select null
from mytable forbidden_row
where forbidden_row.id = mytable.id
and forbidden_row.code not in ('cod', 'F')
);
One of approaches with nested query
SELECT ID,Code, value FROM (
select ID, Code,
(SELECT count(*) FROM TableA a where Code = 'cod' and a.ID = TableA.ID) Cod,
(SELECT count(*) FROM TableA a where Code = 'F' and a.ID = TableA.ID) F,
(SELECT count(*) FROM TableA a where Code not in ('F','cod') and a.ID = TableA.ID) Other,
Value
from TableA
) SOURCE
WHERE Cod <> 0 AND F <> 0 and Other = 0
We can achieve this using CTE. Check this,
-- Split the two record category first, then check cod Or F condition.
WITH Count2 AS (
SELECT id
FROM YourTable
GROUP BY id
HAVING COUNT(id) = 2
),
codORF AS (
SELECT id, code, COUNT(id) FROM YourTable T1
LEFT JOIN Count2 T2 On T1.id = T2.id
WHERE code = 'cod' OR code = 'F'
GROUP BY id, code
Having COUNT(id) = 1
)
-- Finally to take all values
SELECT T1.*
FROM YourTable T1
INNER JOIN codORF T2 ON T1.id = T2.id
with main as (
select *, count(id) over(partition by id order by id) as total_rows
from sample
), next_and_before as (
select *,
COALESCE(lag(code) over(partition by id order by id),lead(code) over(partition by id order by id)) as before_next
from main where total_rows <= 2
)
select * from next_and_before
where lower(trim(concat(code,before_next)))in('codf','fcod','cod','f')
Its a bit of hacky solution:
first you are filtering out all the rows that have less than or equal to 2 rows, since there could be cases where you only have one row per id with a code value = 'f' or 'cod', if you don't want that then simply change the last part to: in ('codf','fcod')
then out of two rows, you are looking at the next and before value and checking if it contains other than 'f' or 'cod'
where clause will filter those out if they exist
Test Results from the link below:
Results of sample data

SQL Server - Set a variable if a value from one table exists in another

I have tables that are set up as shown below:
Table 1:
ID Name Date
1 a 2000-01-01
2 b 2001-01-01
3 c 2002-01-01`
Table 2:
ID Name
2 b
3 c
I would like to return all in Table 1 and then have a column that will hold a variable of either 'Yes' or 'No', based on whether they exist in Table 2, as shown below.
As shown:
Results:
Name Date Yes/No
a 2000-01-01 No
b 2001-01-01 Yes
c 2002-01-01 Yes
I have:
DECLARE #boolean as varchar(10)
IF EXISTS(
SELECT ID FROM Table 2
)
SET #boolean = 'Yes'
ELSE SET #boolean = 'No'
SELECT Name, Date, #boolean as 'Yes/No'
FROM Table 1
LEFT JOIN Table 2 u ON Table 1.ID = Table 2.ID
However, this returns the results as shown below:
Name Date Yes/No
a 2000-01-01 Yes
b 2001-01-01 Yes
c 2002-01-01 Yes
Any ideas on how to manipulate this query to return what is expected?
One option would be to LEFT JOIN the first table to the second one, and then check each record in the first table to see whether its ID matched anything in the second table.
SELECT t1.Name,
t1.Date,
CASE WHEN t2.ID IS NOT NULL THEN 'Yes' ELSE 'No' END AS [Yes/No]
FROM table1 t1
LEFT JOIN table2 t2
ON t1.ID = t2.ID
Demo here:
Rextester
By using LEFT JOIN and checking using CASE if there is no matching result on the second table (t2.ID IS NULL), you can easily get what you need as below:
SELECT Name, Date, CASE WHEN t2.ID IS NULL THEN 'No' ELSE 'Yes' END AS [Yes/No]
FROM Table1 t1 LEFT JOIN Table2 t2 ON t1.ID = t2.ID
If a left join might return multiple unwanted results you can use outer apply() instead:
select t1.*, [Yes/No]=coalesce(x.Yes,'No')
from table_1 t1
outer apply (
select Yes='Yes'
from table_2 t2
where t1.id = t2.id
) x
Select * , case when exists(select 1 from Table2 as b where b.ID = a.ID and b.Name = a.Name) then 'Yes' else 'No' end as 'Yes/No'
From table1 as a
Your query:
DECLARE #boolean as varchar(10)
IF EXISTS(
SELECT ID FROM Table 2
)
SET #boolean = 'Yes'
will always return true because you only check whether ID exists (which always exists records in table2), you forgot to check that should be the ID exists in table1, and you probably do not do in that way, because the indicator is a dynamic value.

Comparing different rows in PostgreSQL for each Id

Few columns in my table looks like
Id Code date latest
1 T 2014-10-04 0
2 B 2014-10-19 0
2 B 2014-10-26 0
1 S 2014-10-05 0
1 T 2014-10-06 0
1 T 2014-10-08 1
2 P 2014-10-27 1
I am tracking all changes made by each ID. if there is any change, I insert new row and update the latest value column.
What I want is for each Id, I should be able to find last code where latest is 0. Also, that code should not be equal to existing code(latest = 1) So for id = 1, answer cannot be
Id Code
1 T
as for id = 1 T is existing code (latest = 1).
So ideally my output should look like:
Id Code
1 S
2 B
I think I can get the latest value for code for each id where latest = 0.
But how do I make sure that it should not be equal to existing code value (latest = 1)
Works in Postgres:
SELECT DISTINCT ON (t0.id)
t0.id, t0.code
FROM tbl t0
LEFT JOIN tbl t1 ON t1.code = t0.code
AND t1.id = t0.id
AND t1.latest = 1
WHERE t0.latest = 0
AND t1.code IS NULL
ORDER BY t0.id, t0.date DESC;
I use the combination of a LEFT JOIN / IS NULL to remove siblings of rows with latest = 1. There are various ways to do this:
Select rows which are not present in other table
Details for DISTINCT ON:
Select first row in each GROUP BY group?
Version with CTE and 2x LEFT JOIN
Since Redshift does not seem to support DISTINCT ON:
WITH cte AS (
SELECT t0.*
FROM tbl t0
LEFT JOIN tbl t1 ON t1.code = t0.code
AND t1.id = t0.id
AND t1.latest = 1
WHERE t0.latest = 0
AND t1.id IS NULL
)
SELECT c0.id, c0.code
FROM cte c0
LEFT JOIN cte c1 ON c1.id = c0.id
AND c1.date > c0.date
WHERE c1.id IS NULL
ORDER BY c0.id;
SQL Fiddle showing both.
I think the following does what you want:
select t.*
from (select distinct on (code) id, code
from table t
where latest = 0
order by code, date desc
) t
where not exists (select 1 from table t2 where t2.id = t.id and t2.code = t.code and t2.latest = 1);
I believe you should have a data for the current version and you should create another table where you would store previous revisions, having foreign key to the Id. Your Id does not fulfill the general expectations for a column with such a name. So, ideally, you would:
create a table Revisions(Id, myTableId, core, date, revision), where Id would be auto_increment primary key and myTableId would point to the Id of the records (1 and 2 in the example)
migrate the elements into revision: insert into Revisions(myTableId, core, date, revision) select Id, core, date latest from MyTable where latest = 0
update the migrated records: update Revisions r1 set r1.revision = (select count(*) from revisions r2 where r2.date < r1.date)
remove the old data from your new table: delete from MyTable where latest = 0
drop your latest column from MyTable
From here, you will be always able to select the penultimate version, or second to last and so on, without problems. Note, that my code suggestions might be of wrong syntax in postgreSQL, as I have never used it, but the idea should work there as well.

How to fetch records in the data given through sql

URN New_Value Old_Value Insert_Timestamp
1 A B 01:00
2 C A 02:00
3 A D 03:00
4 B E 04:00
5 G B 05:00
6 I J 06:00
I need to extract only URN 1 to 6. Because all other URN contains either new_value or old_value which is already used at timestamp earlier than their timestamp.
select t1.*
from MyTable t1
left outer join MyTable t2 on (t1.New_Value = t2.New_Value or t1.New_Value = t2.old_Value)
and t2.Insert_Timestamp < t1.Insert_Timestamp
left outer join MyTable t3 on (t1.Old_Value = t3.Old_Value or t1.Old_Value = t3.New_Value)
and t3.Insert_Timestamp < t1.Insert_Timestamp
where t2.URN is null and t3.URN is null
SQL Fiddle Example
Try to use not exists clause code:
select * from tab t
where not exists
(
select * from tab t2
where t2.Insert_Timestamp<t.Insert_Timestamp
and (t.New_Value=t2.New_Value or t.Old_Value=t2.Old_Value or
t.Old_Value=t2.New_Value or t.New_Value=t2.Old_Value )
)
SQL Fiddle DEMO
Performance will probably be very poor with this schema and requirement but
SELECT *
FROM YourTable T1
WHERE NOT EXISTS (SELECT *
FROM YourTable T2
WHERE T2.Insert_Timestamp < T1.Insert_Timestamp
AND ( T2.New_Value IN ( T1.New_Value, T1.Old_Value )
OR T2.Old_Value IN ( T1.New_Value, T1.Old_Value ) ))
As I don't know what database server you're using, or what your ultimate goal is, I can imagine a generic SQL statement might work. You can use something like this:
SELECT
URN,
New_Value,
Old_Value,
Insert_Timestamp
FROM
table t
WHERE
t.URN BETWEEN 1 AND 6

SQL: Select lowest value that doesn't already exist

In TableA I have an int column.
Is it possible using only a select statement to select the minimum value in the column that DOES NOT EXIST and is greater then 0?
For example, if the col has the values 1,2,9 the select statement will return 3.
If the col has 9,10,11 it will return 1.
I can achieve this using a temp table or using a loop, but I'm wondering if I can do it using just a select statement?
SELECT MIN(t1.ID+1) as 'MinID'
FROM table t1 LEFT JOIN table t2
On t1.ID+1=t2.ID
Where t2.OtherField IS NULL
select
min(nt.id)
from numbertable nt
left outer join originaldata od
on nt.id=od.id
where od.id is null
have a number table that goes from 1 to your max value (or higher)
SELECT DISTINCT x + 1 "val"
EXCEPT SELECT DISTINCT x "val"
ORDER BY "val" ASC
LIMIT 1
What about this?
SELECT Min(id)
FROM (SELECT 1 id
FROM tablea
WHERE 1 NOT IN (SELECT id
FROM tablea)
UNION
SELECT id + 1 id
FROM tablea
WHERE id + 1 NOT IN (SELECT id
FROM tablea)) AS min_ids;
try this:(Updated)
declare #dummy varchar(10) ;
set #dummy =(select top(1) id from dbo.b)
if( #dummy= '1')
begin
select top(1)l.id + 1 as start
from dbo.b as l
left outer join dbo.b as r on l.id + 1 = r.id
where r.id is null
end
else
begin
select '1'
end
Give this a try:
declare #TestTable table (
col int
)
/* Test Case 1: 1,2,9 */
insert into #TestTable
(col)
select 1 union all select 2 union all select 9
SELECT MinValue = (SELECT ISNULL(MAX(t2.col),0)+1
FROM #TestTable t2
WHERE t2.col < t1.col)
FROM #TestTable t1
WHERE t1.col - 1 NOT IN (SELECT col FROM #TestTable)
AND t1.col - 1 > 0
delete from #TestTable
/* Test Case 2: 9,10,11 */
insert into #TestTable
(col)
select 9 union all select 10 union all select 11
SELECT MinValue = (SELECT ISNULL(MAX(t2.col),0)+1
FROM #TestTable t2
WHERE t2.col < t1.col)
FROM #TestTable t1
WHERE t1.col - 1 NOT IN (SELECT col FROM #TestTable)
AND t1.col - 1 > 0
I duplicated my answer from here:
SELECT MIN(a.id) + 1 AS firstfree
FROM (SELECT id FROM table UNION SELECT 0) a
LEFT JOIN table b ON b.id = a.id + 1
WHERE b.id IS NULL
This handles all cases I can think of - including no existing records at all.
The only thing I don't like about this solution is that additional conditions have to be included twice, like that:
SELECT MIN(a.id) + 1 AS firstfree
FROM (SELECT id FROM table WHERE column = 4711 UNION SELECT 0) a
LEFT JOIN table b ON b.column = 4711 AND b.id = a.id + 1
WHERE b.id IS NULL
Please also notice the comments about locking and concurrency - the requirement to fill gaps is in most cases bad design and can cause problems. However, I had a good reason to do it: the IDs are to be printed and typed by humans and we don't want to have IDs with many digits after some time, while all the low ones are free...