How do insert data into a table that already exists? - sql

I'm trying to insert data into a table that already exists, but I cant find anything on how to do this. I only found how to insert this data into a new table.
Syntax error at or near Insert
Tutorial I visited
SELECT film_category.film_id, film_category.category_id, rental_duration, rental_rate
INSERT INTO category_description
FROM film_category
LEFT JOIN FILM
ON film_category.film_id = film.film_id

A simplified test to showcase methods to insert.
CREATE TABLE TableA (
ID INT GENERATED ALWAYS AS IDENTITY,
ColA1 INT,
ColA2 VARCHAR(30)
);
--
-- INSERT VALUES into existing table
--
INSERT INTO TableA (ColA1, ColA2) VALUES
(10, 'A'),
(20, 'B'),
(30, 'C');
3 rows affected
--
-- SELECT INTO new table
--
SELECT ID, ColA1+2 AS ColB1, ColA2||'2' AS ColB2
INTO TableB
FROM TableA;
3 rows affected
--
-- INSERT from SELECT with explicit columns
--
INSERT INTO TableA (ColA1, ColA2)
SELECT ColB1+1, CONCAT(LEFT(ColB2,1),'3') AS ColB23
FROM TableB;
3 rows affected
SELECT * FROM TableA;
id | cola1 | cola2
-: | ----: | :----
1 | 10 | A
2 | 20 | B
3 | 30 | C
4 | 13 | A3
5 | 23 | B3
6 | 33 | C3
--
-- INSERT from SELECT without columns
-- Only works when they have the same number of columns.
--
INSERT INTO TableB
SELECT *
FROM TableA;
6 rows affected
SELECT * FROM TableB;
id | colb1 | colb2
-: | ----: | :----
1 | 12 | A2
2 | 22 | B2
3 | 32 | C2
1 | 10 | A
2 | 20 | B
3 | 30 | C
4 | 13 | A3
5 | 23 | B3
6 | 33 | C3
db<>fiddle here

The order is wrong https://www.w3schools.com/sql/sql_insert_into_select.asp
Also see this answer
Insert into ... values ( SELECT ... FROM ... )
INSERT INTO category_description
SELECT
film_category.film_id,
film_category.category_id,
rental_duration,
rental_rate
FROM
film_category
LEFT JOIN FILM ON film_category.film_id = film.film_id

Related

Compare two tables and show the value of another table if exist, if not exist just show status in SQL

I have two tables to compare in SQL. When the id from one exists in the other, the result I want is the value of data from the second table; when it doesn't exist it will show "Data not Exist" in the 'value' field name.
Example
Table 1
| id|
-----
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10|
Table 2
|id | value
---------
| 1 | 10|
| 2 | 9 |
| 3 | 7 |
| 4 | 8 |
| 5 | 6 |
I've tried the query below:
select a.id,
CASE when exists(select a.id from table2 b where a.id = b.id)
THEN value
else 'Data Not Exist'
END as Result_Value
from table1 a inner join table2 b
on a.id=b.id
order by a.id;
The Result is:
|id | Result_Value
---------
| 1 | 10|
| 2 | 9 |
| 3 | 7 |
| 4 | 8 |
| 5 | 6 |
Above result that's not I wanted, my expectation result like below:
|id | Result_Value
---------
| 1 | 10 |
| 2 | 9 |
| 3 | 7 |
| 4 | 8 |
| 5 | 6 |
| 6 | Data Not Exist |
| 7 | Data Not Exist |
| 8 | Data Not Exist |
| 9 | Data Not Exist |
| 10| Data Not Exist |
Note: This is simple explanation from my query, because my query have complexity to join another table with inner join, I don't know where I'm exactly wrong using select exist.
Just use a LEFT JOIN, and COALESCE any NULL values to Data not Exist:
SELECT a.id, COALESCE(b.value, 'Data not exist') AS value
FROM a
LEFT JOIN b ON b.id = a.id
Output:
id value
1 10
2 9
3 7
4 8
5 Data not exist
6 Data not exist
7 Data not exist
8 Data not exist
9 Data not exist
10 Data not exist
Demo on dbfiddle
I found 2 issues here.
Don't use join/inner join if you want your next table to show up.
DataTypes of your select case values should be the same.
Here's your query.
select a.id,
case
when isnull(b.id, '') != ''
then cast(b.value as varchar(50))
else
'Data Not Exist'
END as Result_Value
from table1 a
left join table2 b on a.id=b.id
order by a.id;
Alternatively, Using LEFT JOIN between Table1 and Table2 and ISNULL to check NULL, If NULL then replace with Data not Exist
SELECT a.id, ISNULL(b.value,'Data not Exist') AS value FROM dbo.Table1 a
LEFT JOIN dbo.Table2 b ON a.id=b.id
You can get the desired results by using a LEFT JOIN a long side with one of:
COALESCE() expression.
ISNULL() function.
CASE expression.
IIF() function.
As the following
SELECT T1.Id,
COALESCE(CAST(T2.Value AS VARCHAR(10)), 'Data Not Exist') ByCoalesce,
ISNULL(CAST(T2.Value AS VARCHAR(10)), 'Data Not Exist') ByIsNull,
CASE WHEN T2.Value IS NULL
THEN 'Data Not Exist'
ELSE CAST(T2.Value AS VARCHAR(10))
END ByCaseExpression,
IIF(T2.Value IS NULL, 'Data Not Exist', CAST(T2.Value AS VARCHAR(10))) ByIifFunction
FROM
(
VALUES
(1),
(2),
(3),
(4),
(5),
(6),
(7),
(8),
(9),
(10)
) T1(Id) LEFT JOIN
(
VALUES
(1, 10),
(2, 9 ),
(3, 7 ),
(4, 8 ),
(5, 6 )
) T2(Id, Value)
ON T1.Id = T2.Id;
Note that you need to CAST() / CONVERT() the INT values to VARCHAR(n) because VARCHAR data type has a lower precedence than INT data type.
Online Demo

how to "deepcopy" rows

My question is similar to this one but more involved. Suppose I have a table A with id idA, and another table B with idB and foreign key idA. I would like to duplicate all entries of A, including corresponding entries in B. For example, if I have the following tables at the start:
A
|---|
|idA|
|---|
| 1 |
| 2 |
| 3 |
|---|
B
|---|---|
|idB|idA|
|---|---|
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
|---|---|
Then the result should be:
A
|---|
|idA|
|---|
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
|---|
B
|---|---|
|idB|idA|
|---|---|
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 4 |
| 5 | 4 |
| 6 | 5 |
|---|---|
This is quite tricky. You need to insert the ids into the a -- but then be able to match them back to the existing ids to insert the right values into b.
A generic solution looks like this:
with i as (
insert into a
select . . . -- the other columns you want
from a
order by idA
returning *
),
a_mapping (
select a.idA, i.idA as new_idA
from (select a.*, row_number() over (order by idA) as seqnum
from a
) a join
(select i.*, row_number() over (order by idA) as seqnum
from i
) i
on a.seqnum = i.seqnum
)
insert into b (idA) (
select am.new_idA
from b join
a_mapping am
on b.idA = am.idA;
Note: If you have another unique column or columns in the row, then the mapping is a little easier to generate. Of course, if you are copying all the columns, then nothing else is unique, so you do need the row_number().
Of course, for your very simple example, you don't need a mapping table. You can just use:
with i as (
insert into a
select . . . -- the other columns you want
from a
order by idA
returning *
)
insert into b (idA) (
select i.idA
from i
I have an approach which may be equivalent to what Gordon Linoff suggests, I would be grateful if you could point out any flaws!
Let's set up the tables:
CREATE TABLE A(
idA SERIAL PRIMARY KEY,
txt varchar);
INSERT INTO A(txt)
VALUES ('A1'), ('A2'),('A3');
CREATE TABLE B(
idB SERIAL PRIMARY KEY,
idA int REFERENCES A(idA),
txt varchar);
INSERT INTO B(idA, txt)
VALUES (1, 'A1.B1'), (1, 'A1.B2'), (2, 'A2.B1');
so the initial data looks as follows:
SELECT * FROM (A LEFT JOIN B ON A.idA=B.idA) ORDER BY A.idA, B.idB;
ida | txt | idb | ida | txt
-----+-----+-----+-----+-------
1 | A1 | 1 | 1 | A1.B1
1 | A1 | 2 | 1 | A1.B2
2 | A2 | 3 | 2 | A2.B1
3 | A3 | | |
(4 rows)
Now, we can use the NEXTVAL function to generate the mappings directly:
CREATE TEMP TABLE tmp_A_new AS (
SELECT *, NEXTVAL('A_idA_seq') as newidA
FROM A ORDER BY idA -- order probably not needed
);
INSERT INTO A(idA, txt) (SELECT newidA, txt FROM tmp_A_new);
CREATE TEMP TABLE tmp_B_new AS (
SELECT B.idB, newidA, B.txt, NEXTVAL('B_idB_seq') as newidB
FROM B, tmp_A_new WHERE B.idA=tmp_A_new.idA ORDER BY idB
);
INSERT INTO B(idB, idA, txt) (SELECT newidB, newidA, txt FROM tmp_B_new);
The results look correct:
SELECT * FROM (A LEFT JOIN B ON A.idA=B.idA) ORDER BY A.idA, B.idB;
ida | txt | idb | ida | txt
-----+-----+-----+-----+-------
1 | A1 | 1 | 1 | A1.B1
1 | A1 | 2 | 1 | A1.B2
2 | A2 | 3 | 2 | A2.B1
3 | A3 | | |
4 | A1 | 4 | 4 | A1.B1
4 | A1 | 5 | 4 | A1.B2
5 | A2 | 6 | 5 | A2.B1
6 | A3 | | |
(8 rows)
Note that this could be continued further down to C, D, etc.
I would be glad for any comments :)

Recursive Iteration in Oracle

I have a table like that:
+----+-----+------+
| id | ord | test |
+----+-----+------+
| 1 | 1 | A |
| 1 | 2 | B |
| 1 | 3 | C |
| 2 | 1 | B |
| 2 | 2 | C |
+----+-----+------+
(Here is some code for creating the data)
drop table temp_test;
create table temp_test (id varchar(20), ord varchar(20), test varchar(20));
insert into temp_test (id,ord,test) values ('1','1','A');
insert into temp_test (id,ord,test) values ('1','2','B');
insert into temp_test (id,ord,test) values ('1','3','C');
insert into temp_test (id,ord,test) values ('2','1','B');
insert into temp_test (id,ord,test) values ('2','2','C');
commit;
How could I get the following result?
+----+-----+-------+
| id | ord | test |
+----+-----+-------+
| 1 | 1 | A |
| 1 | 2 | A_B |
| 1 | 3 | A_B_C |
| 2 | 1 | B |
| 2 | 2 | B_C |
+----+-----+-------+
I have tried using LAG(), something like:
select CONCAT(lag(TEST) over (partition by ID order by ord),TEST) AS TEST from temp_test;
but it does not work recursively.
This code works:
SELECT
R1.*,
( SELECT LISTAGG(test, ';') WITHIN GROUP (ORDER BY ord)
FROM temp_test R2
WHERE R1.ord >= R2.ord
AND R1.ID = R2.ID
GROUP BY ID
) AS WTR_KEYWORD_1
FROM temp_test R1
ORDER BY id, ord;
but it is not performant enough for a larger data set.
Some say the Hierarchical queries are outdated, but they generally perform far better than recursive CTE
SELECT id,
ord,
LTRIM(sys_connect_by_path(test,'_'),'_') as test
FROM temp_test r2 START WITH ord = 1 -- use MIN() to get this if it's not always 1
CONNECT BY PRIOR id = id AND ord = PRIOR ord + 1;
Demo
you can make use of recursive cte to achieve this
with cte(id,ord,test,concat_val)
as (select id,ord,test,test as concat_val
from temp_test
where ord=1
union all
select a.id,a.ord,a.test,b.concat_val||'_'||a.test
from temp_test a
join cte b
on a.id=b.id
and a.ord=b.ord+1
)
select * from cte order by id,ord
Demo here
https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=78baa20f7f364e653899caf63ce7ada2

Insert/update a flag depending on values matched between to tables

I want to make a procedure where:
If an ID exists in table B but not in Table A code with be inser/updated to 0.
Else if the ID exists in both table code field will be insert/updated to 1.
I have two tables, for example:
Table A Table B
+----+--+--+----+------+
| ID | | | Id | Code |
+----+--+--+----+------+
| 1 | | | 1 | 0 |
| 2 | | | 2 | 0 |
| 3 | | | 3 | 0 |
| 4 | | | 4 | 0 |
| | | | 5 | 0 |
| | | | 6 | 0 |
+----+--+--+----+------+
Can you please help me with that?
Table B in the end should be like this:
+----+------+
| Id | Code |
+----+------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 0 |
| 6 | 0 |
+----+------+
Something like this: make left join to generate values of table B, which not present in table A, then merge to update column Code.
merge into table_b
using (select b.id, case when a.id is null then 0 else 1 end code
from table_b b left join table_a a on a.id = b.id) t
on (t.id = table_b.id)
when matched then update
set code = t.code
You can use MERGE:
MERGE into tableB B
USING tableA A
ON (a.id = b.id)
WHEN MATCHED THEN
UPDATE SET code = 1
WHEN NOT MATCHED THEN
INSERT (id,code) values (a.id, 0)
Please find the answer below for your question...,
create table TABLEA (ID int);
insert into TABLEA (ID) values (1);
insert into TABLEA (ID) values (2);
insert into TABLEA (ID) values (3);
insert into TABLEA (ID) values (4);
insert into TABLEA (ID) values (5);
create table TABLEB (ID int, code int);
insert into TABLEB (ID,code) values (1,0);
insert into TABLEB (ID,code) values (2,0);
insert into TABLEB (ID,code) values (3,0);
insert into TABLEB (ID,code) values (4,0);
insert into TABLEB (ID,code) values (5,0);
insert into TABLEB (ID,code) values (6,0);
insert into TABLEB (ID,code) values (7,0);
insert into TABLEB (ID,code) values (8,0);
UPDATE TABLEB SET CODE=1 FROM TABLEB B INNER JOIN TABLEA as A on A.id=B.id;
SELECT * FROM TABLEB;
ID CODE
1 1
2 1
3 1
4 1
5 1
6 0
7 0
8 0

Create New Table From Other Table After Grouping

How can I insert to a table a value from "grouping" other table?
That means I have 2 table with different structure.
The table ORDRE with existed DATA
Table ORDRE:
ORDRE ID | CODE_DEST |
-------------------------
1 | a |
2 | b |
3 | c |
4 | a |
5 | a |
6 | b |
7 | g |
I want to INSERT the value FROM Table ORDRE INTO TABLE VOIT:
ID_VOIT | ORDRE ID | CODE_DEST |
---------------------------------------
1 | 1 | a |
1 | 4 | a |
1 | 5 | a |
2 | 2 | b |
2 | 6 | b |
3 | 3 | c |
4 | 7 | g |
This is my best guess on what you need using only the info available.
declare #Ordre table
(
ordre_id int,
code_dest char(1)
)
declare #Voit table
(
id_voit int,
ordre_id int,
code_dest char(1)
)
insert into #Ordre values
(1,'a'),
(2,'b'),
(3,'c'),
(4,'a'),
(5,'a'),
(6,'b'),
(7,'g')
insert into #Voit
select id_voit, ordre_id, rsOrdre.code_dest
from #Ordre rsOrdre
inner join
(
select code_dest, ROW_NUMBER() over (order by code_dest) as id_voit
from #Ordre
group by code_dest
) rsVoit on rsVoit.code_dest = rsOrdre.code_dest
order by id_voit, ordre_id
select * from #Voit
Working Example.
For the specific data you give as an example, this works:
insert into VOIT
select
case code_dest
when 'a' then 1
when 'b' then 2
when 'c' then 3
when 'g' then 4
else 0
end, orderId, code_dest from ORDRE order by code_dest, orderId
But it kind of sucks because it requires hard-coding in a huge case statement.
Test is here - https://data.stackexchange.com/stackoverflow/q/119442/
What I like more is moving the VOIT ID / Code_Dest associations to a new table, so then you could do an inner join instead.
insert into VOIT
select voit_id, orderId, t.code_dest
from ORDRE t
join Voit_CodeDest t2 on t.code_dest = t2.code_dest
order by code_dest, orderId
Working example of that here - https://data.stackexchange.com/stackoverflow/q/119443/