SQL Server : update and insert in one query - sql

I have some trouble trying to create an update/insert query.
From a .CSV, I create a temporary table (heading and one datarow as a example):
sku
product_id
description_en
description_ru
description_lv
EE1010
4633
Description in Eng
Description in Rus
Description in Lat
I intend to iterate over each row and update/insert rows into another table with this query:
UPDATE ProductLocalized
SET FullDescription = (CASE
WHEN LanguageID = 7 THEN description_en
WHEN LanguageID = 12 THEN description_ru
WHEN LanguageID = 14 THEN description_lv
END)
WHERE LanguageID IN (7, 12, 14)
AND ProductID = product_id;
My problem is how to add the INSERT part if some of the languages missing?

You can use Upsert in SQL to achieve this. Please find below quick example for the same.
create table dbo.test_source
(
id int identity(1,1),
language varchar(50),
description varchar(100)
)
create table dbo.test_dest
(
id int identity(1,1),
language varchar(50),
description varchar(100)
)
Insert into dbo.test_source values ('English', 'British language')
Insert into dbo.test_source values ('Hindi', 'Indian language')
Insert into dbo.test_source values ('Chinese', 'China')
Insert into dbo.test_dest values ('English', 'British language')
Insert into dbo.test_dest values ('Hindi', 'NA')
SELECT * FROM dbo.test_Source
SELECT * FROM dbo.test_Dest
Result
id language description
----------- -------------------------------------------------- ----------------------------------------------------------------------------------------------------
1 English British language
2 Hindi Indian language
3 Chinese China
id language description
----------- ------------------ -------------
1 English British language
2 Hindi NA
MERGE dbo.test_dest as MyTarget
USING
(
SELECT
ID,
Language,
Description
FROM dbo.test_source
) as MySource
ON MyTarget.Language = MySource.Language
WHEN MATCHED AND NOT
(
MySource.Description = ISNULL(MyTarget.Description, '')
)
THEN
UPDATE
Set MyTarget.Description = MySource.Description
WHEN NOT MATCHED BY TARGET
THEN
INSERT (Language, description)
VALUES (MySource.Language
,MySource.Description);
SELECT * FROM dbo.test_Dest
Result
id language description
----------- -------------------------------------------------- ----------------------------------------------------------------------------------------------------
1 English British language
2 Hindi Indian language
3 Chinese China
We can see record with 2 got updated with source table description and record with id 3 got inserted as it was not exist into destination table.

You can use IF EXIST or IF NOT EXIST statements to filter the records and then apply the INSERT or UPDATE Commands.
Example:
IF NOT EXIST ( Condition ) { INSERT }
IF EXIST ( Condition ) { UPDATE }
An alternate way is:
IF EXIST( Condition )
update
ELSE
insert

Related

Why "=" in where clause, not match exact string (including spaces and special characters)?

Why "=" in where clause, not match exact string (including spaces and special characters).
In this query I applied '$' Symbol for filtering repeated data on Data Table and null treated as empty strings. This query will return sum of all values inside one group in front of group name.
Issue is when data/string have spaces or special characters like "." in it it will not match these string in where clause and show null in front of these group instead of their some.
Schema (PostgreSQL v13)
CREATE TABLE IF NOT EXISTS products (
id int NOT NULL,
title varchar(200) NOT NULL,
description varchar(200) NOT NULL,
price int NOT NULL,
PRIMARY KEY (id)
);
INSERT INTO products VALUES
(1, 'test', 'test',2222),
(2, 'test', 'test2',1111),
(3, 'test3', 'test3',1111),
(4, 'test3.2', 'test3.2',555),
(5, 'test3.2', 'test3.3',1111),
(6, 'test4', 'test4 desc',1111);
Query #1
create or replace function get_price_value(
tablename regclass,
sum_of_column_name character varying,
on_column_name character varying,
on_column_value text
)
returns int as
'
declare total_sum integer;
begin
EXECUTE FORMAT(''select sum(%I) from %I where %I=''''%I'''' ''
,sum_of_column_name
,tablename
,on_column_name
,on_column_value)
INTO total_sum;
return total_sum;
end;
'
language plpgsql;
There are no results to be displayed.
Query #2
select id,
title,
description,
price
from(
select DISTINCT id, title, description, price,trno, drno
from (
select id, 1, null, title, null, get_price_value('products'::regclass,'price','title',title)::varchar as price, 1 as trno, 2 as drno from products
union all
select id, 2, null, concat(title,'$$$'), description, get_price_value('products'::regclass,'price','description',description)::varchar as price, 2 as trno, 1 as drno from products
union all
select id, 3, id::varchar, concat(title,'$$$') as title, concat(description,'$$$') as description, price::varchar, 2 as trno, 2 as drno from products
) temp1 (xid, xord, id, title, description,price,trno,drno)
order by title, trno, description, drno
) as temp2;
id
title
description
price
test
3333
test$$$
test
2222
1
test$$$
test$$$
2222
test$$$
test2
1111
2
test$$$
test2$$$
1111
test3
1111
test3$$$
test3
1111
3
test3$$$
test3$$$
1111
test3.2
test3.2$$$
test3.2
4
test3.2$$$
test3.2$$$
555
test3.2$$$
test3.3
5
test3.2$$$
test3.3$$$
1111
test4
1111
test4$$$
test4 desc
6
test4$$$
test4 desc$$$
1111
Output View in Data Table
Expected Output:
Please suggest any solution in sql. Thank you!
View on DB Fiddle
The problem is the FORMAT(''where %I=''''%I'''' '' part. The second %I isn't an identifier, but a literal for which you should use %L without the enclosing '.

Table Variable and Table-Valued Function equivalent in PostgreSQL

I need to create a function in PostgreSQL for the following :
Query multiple tables based on a business logic (all result sets return the same type of data)
Compile all result sets into one table and return that table
Is it possible to accomplish this without using the temp tables in PostgreSQL?
I currently do this in Microsoft SQL server using Table Variables, below is a sample function:
create FUNCTION test(#search_in nvarchar(500))
RETURNS #data_table TABLE
(
item_id int,
item_type nvarchar(1),
first_name nvarchar(100),
last_name nvarchar(100))
) AS
BEGIN
-- from first table
if charindex('search_in_authors', #search_in) > 0
insert into #data_table
select item_id, 'a', first_name, last_name
from authors
where first_name = 'james'
-- from second table
if charindex('search_in_editors', #search_in) > 0
insert into #data_table
select item_id, 'e', first_name, last_name
from editors
where first_name = 'james'
-- from third table
if charindex('search_in_publishers', #search_in) > 0
insert into #data_table
select item_id, 'p', first_name, last_name
from publishes
where first_name = 'james'
-- there could be more like these based on the business logic...
(...)
-- finally return the records compiled in #data_table
RETURN
END
Sample calls to the function:
select * from dbo.test('search_in_authors')
select * from dbo.test('search_in_authors, search_in_editors')
select * from dbo.test('search_in_authors, search_in_editors,search_in_publishers ')
Are there any options in PostgreSQL to achieve this other than using a temp table ?
Thanks,
San
You can use RETURN QUERY to add the result of various queries to the output.
CREATE OR REPLACE FUNCTION public.testf()
RETURNS TABLE(id INTEGER, name text)
STABLE
AS $$
DECLARE
BEGIN
RETURN QUERY select 1 as id, 'abc' as name;
RETURN QUERY select 2 as id, 'def' as name;
RETURN QUERY select 3 as id, 'xyz' as name;
-- Final return as set is now complete.
return;
END;
$$ LANGUAGE plpgsql;
select * from public.testf();
id | name
----+------
1 | abc
2 | def
3 | xyz
(3 rows)

How to insert Auto-Increment using SELECT INTO Statement? SQL SERVER

This my table1:
Name Description
john student
dom teacher
I need to use SELECT * INTO to transfer it to another table (table2) but I want it with a new column named Auto which is auto-incremented.
Which will look like this:
Name Description Auto
John Student 1
Dom Teacher 2
Current Code: SELECT * INTO table2 FROM table1
Use ROW_NUMBER to add sequential number starting from 1.
SELECT *,
Auto = ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
INTO table2
FROM table1
The accepted answer has additional convenience when breaking one table into several smaller ones with the exact number of rows. If necessary, it is possible to remove the column used for autoincrement.
SELECT *,
ID = ROW_NUMBER() OVER(ORDER BY ( SELECT NULL ))
INTO #Table2
FROM Table1
DECLARE #start INT, #end INT;
SET #start = 1;
SET #end = 5000000;
SELECT *
INTO Table3
FROM #Table2
WHERE ID BETWEEN #start AND #end;
ALTER TABLE Table3 DROP COLUMN ID;
You can use an identity field for this, that's what they're for. The logic of the identity(1,1) means that it will start at the number 1 and increment by 1 each time.
Sample data;
CREATE TABLE #OriginalData (Name varchar(4), Description varchar(7))
INSERT INTO #OriginalData (Name, Description)
VALUES
('John','student')
,('Dom','teacher')
Make a new table and insert the data into it;
CREATE TABLE #NewTable (Name varchar(4), Description varchar(7), Auto int identity(1,1))
INSERT INTO #NewTable (Name, Description)
SELECT
Name
,Description
FROM #OriginalData
Gives the results as;
Name Description Auto
John student 1
Dom teacher 2
If you ran the insert a couple more times your results would look like this;
Name Description Auto
John student 1
Dom teacher 2
John student 3
Dom teacher 4
John student 5
Dom teacher 6

SQL 2 Inserts based on return of first

I am generating a series of Inserts based on data from an Excel file into SQL Server 2014
How do I get the value of the ID of the first INSERT to put into the second
Simplified example where Ontario is a Province of Canada:
Insert into country (Name) values('canada');
Insert into provinces (CountryId, Name) values (???,'ontario');
There are 100 inserts so performance is not an issue.
declare #countryid int
Insert into country (Name) values('canada');
SELECT #countryid = SCOPE_IDENTITY()
Insert into provinces (CountryId, Name) values (#countryid,'ontario');
the answer above from tshoemake shows how you can insert one record and get the result. If you want to insert many records in Country and then many records in provinces, you might want to have a look at the OUTPUT clause. You'll have to work out how to join in your list of provinces because this code will just add Ontario to every country:
create table __country
(
id int identity(1,1) primary key,
Name varchar(5000)
)
CREATE TABLE __Provinces (
countryid int,
name varchar(5000)
)
CREATE TABLE #tempIDs
(
id int
)
INSERT INTO __Country
OUTPUT inserted.id
INTO #tempIDs
values ('canada'), values('USA')
insert into __Provinces
select #tempIDs.id, 'ontario'
from #tempIDs
join __country
ON __country.id = #tempIDs.id
select * from __Provinces

Data deduplificaton

The code below explains best what I'm trying to accomplish. I know that I can use a cursor or other looping routine to loop through the records to find the duplicates and create my notes records based on what is found. I'm trying to avoid that, unless there's no better option.
DROP TABLE #orig
DROP TABLE #parts
DROP TABLE #part_notes
CREATE TABLE #orig(partnum VARCHAR(20), notes VARCHAR(100));
INSERT INTO #orig VALUES ('A123', 'To be used on Hyster models only')
INSERT INTO #orig VALUES ('A123', 'Right Hand model only')
INSERT INTO #orig VALUES ('A125', 'Not to be used by Jerry')
INSERT INTO #orig VALUES ('A125', NULL)
INSERT INTO #orig VALUES ('A125', 'asdfasdlfj;lsdf')
INSERT INTO #orig VALUES ('A128', 'David test')
INSERT INTO #orig VALUES ('A129', 'Fake part')
SELECT COUNT(*) FROM #orig
-- SHOW ME UNIQUE PARTS, MY PARTS TABLE SHOULD BE UNIQUE!
SELECT DISTINCT partnum FROM #orig
CREATE TABLE #parts(id INT IDENTITY(1,1), partnum VARCHAR(20));
INSERT INTO #parts
SELECT DISTINCT partnum FROM #orig
SELECT * FROM #parts
CREATE TABLE #part_notes(id INT IDENTITY(1,1), part_id INT, line_number INT, notes VARCHAR(100));
/*
HOW DO I AT THIS POINT POPULATE the #part_notes table so that it looks like this:
(note: any NULL or empty note strings should be ignored)
id part_id line_number notes
1 1 1 To be used on Hyster models only
2 1 2 Right Hand model only
3 2 1 Not to be used by Jerry
4 2 2 asdfasdlfj;lsdf
6 3 1 David test
7 4 1 Fake part
*/
The below just arbitrarily chooses line_numbers as there doesn't seem to be anything suitable to order by in the data.
SELECT p.id part_id,
p.partnum ,
ROW_NUMBER() over (partition BY p.id ORDER BY (SELECT 0)) line_number,
notes
FROM #parts p
JOIN #orig o
ON o.partnum=p.partnum
WHERE notes IS NOT NULL
AND notes <> ''
ORDER BY part_id