Insert into temporary table from select returns blank table - tsql

I'm trying to fill two temporary tables with ids coming from outside as a single string that I split and save to a third temporary table:
CREATE TABLE #TempProdotti (Id int NULL);
CREATE TABLE #TempProdottiAggregati (Id int NULL);
CREATE TABLE #TempCorsiSingoli (Id int NULL);
-- split ids and cast them as INT
INSERT INTO #TempProdotti (Id)
(SELECT CAST(value AS int) AS Id
FROM string_split('3116,3122,3090', ','));
-- then search into products table if the ids match any aggregated (or not) product.
-- then save aggegated products id in one table and the not aggregated ones into another
INSERT INTO #TempCorsiSingoli (Id)
(SELECT Id
FROM mod_SHOP_Prodotti
WHERE Id IN (SELECT Id FROM #TempProdotti)
AND ProdottoAggregato = 0);
INSERT INTO #TempProdottiAggregati (Id)
(SELECT Id
FROM mod_SHOP_Prodotti
WHERE Id IN (SELECT Id FROM #TempProdotti)
AND ProdottoAggregato = 1);
SELECT *
FROM #TempProdotti;
SELECT *
FROM #TempProdottiAggregati;
SELECT *
FROM #TempCorsiSingoli;
DROP TABLE #TempProdotti;
DROP TABLE #TempProdottiAggregati;
DROP TABLE #TempCorsiSingoli;
When I run the query, if it doesn't find anything in one of the two temporary tables, it just returns an empty table:
Is there a clean way to return NULL on Id in case the condition is not met?

One method would be to LEFT JOIN from a data set with row of NULL values:
SELECT TP.*
FROM (VALUES(NULL))V(N)
LEFT JOIN #TempProdotti TP ON 1 = 1;
If #TempProdotti contains rows, then the data in the table will be returned. If not a single row, of NULLs will be returned.
CREATE TABLE #TempProdotti (Id int NULL);
CREATE TABLE #TempProdottiAggregati (Id int NULL);
CREATE TABLE #TempCorsiSingoli (Id int NULL);
GO
INSERT INTO #TempProdotti (Id)
SELECT CAST(value AS int) AS Id
FROM string_split('3116,3122,3090', ',');
INSERT INTO #TempCorsiSingoli (Id)
SELECT Id
FROM mod_SHOP_Prodotti
WHERE Id IN (SELECT Id FROM #TempProdotti)
AND ProdottoAggregato = 0;
INSERT INTO #TempProdottiAggregati (Id)
SELECT Id
FROM mod_SHOP_Prodotti
WHERE Id IN (SELECT Id FROM #TempProdotti)
AND ProdottoAggregato = 1;
GO
SELECT TP.*
FROM (VALUES (NULL)) V (N)
LEFT JOIN #TempProdotti TP ON 1 = 1;
SELECT TPA.*
FROM (VALUES (NULL)) V (N)
LEFT JOIN #TempProdottiAggregati TPA ON 1 = 1;
SELECT TCS.*
FROM (VALUES (NULL)) V (N)
LEFT JOIN #TempCorsiSingoli TCS ON 1 = 1;
GO
DROP TABLE #TempProdotti;
DROP TABLE #TempProdottiAggregati;
DROP TABLE #TempCorsiSingoli;

Related

Copying records in a table with self referencing ids

I have a table with records which can reference another row in the same table so there is a parent-child relationship between rows in the same table.
What I am trying to achieve is to create the same data for another user so that they can see and manage their own version of this structure through the web ui where these rows are displayed as a tree.
Problem is when I bulk insert this data by only changing user_id, I lose the relation between rows because the parent_id values will be invalid for these new records and they should be updated as well with the newly generated ids.
Here is what I tried: (did not work)
Iterate over main_table
copy-paste the static values after each
do another insert on a temp table for holding old and new ids
update old parent_ids with new ids after loop ends
My attempt at doing such thing(last step is not included here)
create or replace function test_x()
returns void as
$BODY$
declare
r RECORD;
userId int8;
rowPK int8;
begin
userId := (select 1)
create table if not exists id_map (old_id int8, new_id int8);
create table if not exists temp_table as select * from main_table;
for r in select * from temp_table
loop
rowPK := insert into main_table(id, user_id, code, description, parent_id)
values(nextval('hibernate_sequence'), userId, r.code, r.description, r.parent_id) returning id;
insert into id_map (old_id, new_id) values (r.id, rowPK);
end loop;
end
$BODY$
language plpgsql;
My PostgreSQL version is 9.6.14.
DDL below for testing.
create table main_table(
id bigserial not null,
user_id int8 not null,
code varchar(3) not null,
description varchar(100) not null,
parent_id int8 null,
constraint mycompkey unique (user_id, code, parent_id),
constraint mypk primary key (id),
constraint myfk foreign key (parent_id) references main_table(id)
);
insert into main_table (id, user_id, code, description, parent_id)
values(0, 0, '01', 'Root row', null);
insert into main_table (id, user_id, code, description, parent_id)
values(1, 0, '001', 'Child row 1', 0);
insert into main_table (id, user_id, code, description, parent_id)
values(2, 0, '002', 'Child row 2', 0);
insert into main_table (id, user_id, code, description, parent_id)
values(3, 0, '002', 'Grand child row 1', 2);
How to write a procedure to accomplish this?
Thanks in advance.
It appears your task is coping all data for a given user to another while maintaining the hierarchical relationship within the new rows. The following accomplishes that.
It begins creating a new copy of the existing rows with the new user_id, including the old row parent_id. That will be user in the next (update) step.
The CTE logically begins with the new rows which have parent_id and joins to the old parent row. From here it joins to the old parent row to the new parent row using the code and description. At that point we have the new id along with the new parent is. At that point just update with those values. Actually for the update the CTE need only select those two columns, but I've left the intermediate columns so you trace through if you wish.
create or replace function copy_user_data_to_user(
source_user_id bigint
, target_user_id bigint
)
returns void
language plpgsql
as $$
begin
insert into main_table ( user_id,code, description, parent_id )
select target_user_id, code, description, parent_id
from main_table
where user_id = source_user_id ;
with n_list as
(select mt.id, mt.code, mt.description, mt.parent_id
, mtp.id p_id,mtp.code p_code,mtp.description p_des
, mtc.id c_id, mtc.code c_code, mtc.description c_description
from main_table mt
join main_table mtp on mtp.id = mt.parent_id
join main_table mtc on ( mtc.user_id = target_user_id
and mtc.code = mtp.code
and mtc.description = mtp.description
)
where mt.parent_id is not null
and mt.user_id = target_user_id
)
update main_table mt
set parent_id = n_list.c_id
from n_list
where mt.id = n_list.id;
return;
end ;
$$;
-- test
select * from copy_user_data_to_user(0,1);
select * from main_table;
CREATE TABLE 'table name you want to create' SELECT * FROM myset
but new table and myset column name should be equal and you can also
use inplace of * to column name but column name exist in new table
othwerwise getting errors

DELETE WHERE NOT EXISTS Not Being Applied Correctly

I'm using NOT EXISTS during a DELETE statement in a stored procedure and the not exists is not being applied to the data.
Using the following example data:
CREATE TABLE Region
(
RegionID INT IDENTITY(1,1)
,RegionName VARCHAR(25)
)
GO
INSERT INTO Region(RegionName)
VALUES ('East Coast')
,('Mid West')
,('West Coast')
GO
CREATE TABLE Customer
(
CustomerID INT IDENTITY(1,1)
,FirstName VARCHAR(5)
,Region INT
)
GO
INSERT INTO Customer(FirstName,Region)
VALUES('Tom',1)
,('Mike',2)
,('Jean',3)
GO
CREATE TABLE Orders
(
OrderID INT IDENTITY(1,1)
,CustomerID INT
,OrderAmount INT
,OrderDate DATE
)
GO
INSERT INTO Orders(CustomerID,OrderAmount,OrderDate)
VALUES(1,10,'2018-11-30')
,(2,12,'2018-11-30')
,(2,15,'2018-12-01')
,(2,8,'2018-12-02')
,(2,11,'2018-12-03')
,(3,13,'2018-12-01')
,(3,20,'2018-12-03')
GO
Using that data I'm trying to create a procedure that does the following:
CREATE PROCEDURE udsp_GetOrdersOfXAmount #OrderAmount INT, #RegionID INT = 0
AS
BEGIN
DECLARE #ProcedureTemp TABLE
(
OrderID INT
,CustomerID INT
,OrderAmount INT
,OrderDate DATE
)
INSERT INTO #ProcedureTemp(OrderID,CustomerID,OrderAmount,OrderDate)
SELECT * FROM Orders WHERE OrderAmount >= #OrderAmount
--Do several other UPDATES/ DELETES to #ProcedureTemp
--This is where the issue lies
IF #RegionID > 0
BEGIN
DELETE T FROM #ProcedureTemp T
WHERE NOT EXISTS
(
SELECT *
FROM Customer C
JOIN #ProcedureTemp T ON T.CustomerID = C.CustomerID
WHERE C.Region = #RegionID
)
END
SELECT * FROM #ProcedureTemp
END
GO
If you execute the procedure with the #RegionID parameter populated, you will see the procedure is not honoring the filter by region.
E.G.
EXEC udsp_GetOrdersOfXAmount 10,3
However, if you run the sub query used in the DELETE statement as its own query, you will see the WHERE clause logic provided is working. I don't understand why the it isn't working when used with NOT EXISTS in the DELETE statement.
DECLARE #OrderAmount INT = 10, #RegionID INT = 3
DECLARE #ProcedureTemp TABLE
(
OrderID INT
,CustomerID INT
,OrderAmount INT
,OrderDate DATE
)
INSERT INTO #ProcedureTemp(OrderID,CustomerID,OrderAmount,OrderDate)
SELECT * FROM Orders WHERE OrderAmount >= #OrderAmount
SELECT *
FROM Customer C
JOIN #ProcedureTemp T ON T.CustomerID = C.CustomerID
WHERE C.Region = #RegionID
Thank you in advance for any help you can provide.
You don't need the join in the inner query.
The fact that you are using the same alias for the outer query and the inner one is confusing to me, I'm guessing SQL Server also should have a problem with it.
Try writing it like this:
DELETE T
FROM #ProcedureTemp T
WHERE NOT EXISTS
(
SELECT *
FROM Customer C
-- You already have the T from the outer statement
WHERE T.CustomerID = C.CustomerID
AND C.Region = #RegionID
)

PostgreSql: how to update table from array?

How to update table fields from array to avoid temp table using?
User passes array to reorder table.
create TABLE items
(
id serial primary key,
name text,
select_order int
)
insert into items(name, select_order)
values
('cucumber',0),
('milk',1),
('coffee',2),
('water',3)
select * from items order by select_order;
DO
$body$
DECLARE var_array int[]:='{3,0,2,1}';
BEGIN
update items ??
END;
$body$
The final result in this example should be
select * from items order by select_order;
name select_order
------------------------
water 0
cucumber 1
coffee 2
milk 3
Assuming the index in the array corresponds to the value in select_order the following query returns the new and old value:
select i.id, i.select_order, t.new_sort
from items i
join lateral unnest(array[3,0,2,1]) with ordinality as t(new_sort, idx) on t.idx - 1 = i.select_order
Now this can be used to update the target table:
update items
set select_order = r.new_sort
from (
select i.id, i.select_order, t.new_sort
from items i
join lateral unnest(array[3,0,2,1]) with ordinality as t(new_sort, idx) on t.idx - 1 = i.select_order
) r
where r.id = items.id;
This also assumes that select_order starts a 0 and has no gaps.

T-SQL: Determine upline and downline from hierarchy without the Parent ID

I have this self-referencing table where-in I should get the upline and downline and hierarchy levels without the Parent ID provided.
Any ideas?
Have you tried using recursive CTE?
for example:
assume you have table tbl(EmpId, Name, MngrId) which has self-referencing relationship
create table tbl
(
EmpId int not null,
Name nvarchar(100),
MngrId int null)
insert into tbl(EmpId, Name, MngrId)
values (1,'Adel',Null),
(2,'Ali',1),
(3,'Shaban',1),
(4,'Mark',3),
(5,'John',3),
(6,'Tony',Null),
(7,'Peter',6)
You can create some view like that:
create view Employees
Begin
with cte
as
(
Select EmpId,Name, Null as MngrId, cast(null as nvarchar(100)) as MngrName, 1 as EmpLevel
from tbl
where MngrId is Null
Union All
Select t.EmpId, t.Name, c.EmpId as MngrId, c.Name as MngrName, c.EmpLevel + 1 as EmpLevel
from tbl t
inner join cte c
on t.MngrId = c.EmpId
)
Select *
from cte
order by EmpLevel, EmpId
End
You can now use EmpLevel to jump between different levels and MngrName to get information about parent node

Copy content in TSQL

I need to copy content from one table to itself and related tables... Let me schematize the problem. Let's say I have two tables:
Order
OrderID : int
CustomerID : int
OrderName : nvarchar(32)
OrderItem
OrderItemID : int
OrderID : int
Quantity : int
With the PK being autoincremental.
Let's say I want to duplicate the content of one customer to another. How do I do that efficiently?
The problem are the PKs. I would need to map the values of OrderIDs from the original set of data to the copy in order to create proper references in OrderItem. If I just select-Insert, I won't be able to create that map.
Suggestions?
For duplicating one parent and many children with identities as the keys, I think the OUTPUT clause can make things pretty clean (SqlFiddle here):
-- Make a duplicate of parent 1, including children
-- Setup some test data
create table Parents (
ID int not null primary key identity
, Col1 varchar(10) not null
, Col2 varchar(10) not null
)
insert into Parents (Col1, Col2) select 'A', 'B'
insert into Parents (Col1, Col2) select 'C', 'D'
insert into Parents (Col1, Col2) select 'E', 'F'
create table Children (
ID int not null primary key identity
, ParentID int not null references Parents (ID)
, Col1 varchar(10) not null
, Col2 varchar(10) not null
)
insert into Children (ParentID, Col1, Col2) select 1, 'g', 'h'
insert into Children (ParentID, Col1, Col2) select 1, 'i', 'j'
insert into Children (ParentID, Col1, Col2) select 2, 'k', 'l'
insert into Children (ParentID, Col1, Col2) select 3, 'm', 'n'
-- Get one parent to copy
declare #oldID int = 1
-- Create a place to store new ParentID
declare #newID table (
ID int not null primary key
)
-- Create new parent
insert into Parents (Col1, Col2)
output inserted.ID into #newID -- Capturing the new ParentID
select Col1, Col2
from Parents
where ID = #oldID -- Only one parent
-- Create new children using the new ParentID
insert into Children (ParentID, Col1, Col2)
select n.ID, c.Col1, c.Col2
from Children c
cross join #newID n
where c.ParentID = #oldID -- Only one parent
-- Show some output
select * from Parents
select * from Children
Do you have to have the primary keys from table A as primaries in Table B? If not you can do a select statement with an insert into. Primary Key's are usually int's that start from an ever increasing seed (identity). Going around this and declaring an insert of this same data problematically has the disadvantage of someone thinking this is a distinct key set on this table and not a 'relationship' or foreign key value.
You can Select Primary Key's for inserts into other tables, just not themselves.... UNLESS you set the 'identity insert on' hint. Do not do this unless you know what this does as you can create more problems than it's worth if you don't understand the ramifications.
I would just do the ole:
insert into TableB
select *
from TableA
where (criteria)
Simple example (This assumes SQL Server 2008 or higher). My bad I did not see you did not list TSQL framework. Not sure if this will run on Oracle or MySql.
declare #Order Table ( OrderID int identity primary key, person varchar(8));
insert into #Order values ('Brett'),('John'),('Peter');
declare #OrderItem Table (orderItemID int identity primary key, OrderID int, OrderInfo varchar(16));
insert into #OrderItem
select
OrderID -- I can insert a primary key just fine
, person + 'Stuff'
from #Order
select *
from #Order
Select *
from #OrderItem
Add an extra helper column to Order called OldOrderID
Copy all the Order's from the #OldCustomerID to the #NewCustomerID
Copy all of the OrderItems using the OldOrderID column to help make the relation
Remove the extra helper column from Order
ALTER TABLE Order ADD OldOrderID INT NULL
INSERT INTO Order (CustomerID, OrderName, OldOrderID)
SELECT #NewCustomerID, OrderName, OrderID
FROM Order
WHERE CustomerID = #OldCustomerID
INSERT INTO OrderItem (OrderID, Quantity)
SELECT o.OrderID, i.Quantity
FROM Order o INNER JOIN OrderItem i ON o.OldOrderID = i.OrderID
WHERE o.CustomerID = #NewCustomerID
UPDATE Order SET OldOrderID = null WHERE OldOrderID IS NOT NULL
ALTER TABLE Order DROP COLUMN OldOrderID
IF the OrderName is unique per customer, you could simply do:
INSERT INTO [Order] ([CustomerID], [OrderName])
SELECT
2 AS [CustomerID],
[OrderName]
FROM [Order]
WHERE [CustomerID] = 1
INSERT INTO [OrderItem] ([OrderID], [Quantity])
SELECT
[o2].[OrderID],
[oi1].[Quantity]
FROM [OrderItem] [oi1]
INNER JOIN [Order] [o1] ON [oi1].[OrderID] = [o1].[OrderID]
INNER JOIN [Order] [o2] ON [o1].[OrderName] = [o2].[OrderName]
WHERE [o1].[CustomerID] = 1 AND [o2].[CustomerID] = 2
Otherwise, you will have to use a temporary table or alter the existing Order table like #LastCoder suggested.