How to remove sql records using PowerShell - sql-server

I have this simple script that compare the two table and I'm just wondering how can I modify it so that it'll delete if there is a difference in Hold_Inv table.
So basically my Temp_Hold_Inv contain the latest data and I wanted to always in sync with my Hold_Inv table.
Let say a particular user is removed from Temp_Hold_Inv table but the data is already Existed in Hold_inv and Daily_Proc table then removed from those two table too after comparison.
Here I'm able to get the difference but not sure how to use $comparedResult to delete all the difference from Hold_Inv and Daily_Proc Table
$query2 = "
SELECT 'Temp_Hold_Inv' AS SRC, T1.*
FROM (
SELECT Hold, GID, Source FROM Temp_Hold_Inv
EXCEPT
SELECT Hold, GID, Source FROM Hold_Inv
) AS T1
UNION ALL
SELECT 'Hold_Inv' AS SRC, T2.*
FROM (
SELECT Hold, GID, Source FROM Hold_Inv
EXCEPT
SELECT Hold, GID, Source FROM Temp_Hold_Inv
) AS T2
;"
$compareResult = Invoke-Sqlcmd -Query $query2 -ServerInstance $Server -Database $Database
Any help or suggestion will be really appreciated.

You need a MERGE statement, but you need to work out what is the primary key of the table in order to match rows up.
$query2 = "
MERGE Hold_Inv WITH (HOLDLOCK) AS t
USING Temp_Hold_Inv AS s
ON t.SomePrimaryKey = s.SomePrimaryKey
WHEN MATCHED AND (
t.Hold <> s.Hold
OR t.Source <> s.Source)
THEN UPDATE SET
Hold = s.Hold
Source = s.Source
WHEN NOT MATCHED BY TARGET
THEN INSERT (Hold, GID, Source)
VALUES (Hold, GID, Source)
WHEN NOT MATCHED BY SOURCE
THEN DELETE
-- you can get output like this
-- OUTPUT $action, inserted.*, deleted.*
;"
Invoke-Sqlcmd -Query $query2 -ServerInstance $Server -Database $Database

Related

How to retain column order using listagg in snowflake

I have a Snowflake table A as below:
CREATE or replace table TABLEA
(
Address VARCHAR(50),
Zip VARCHAR(50),
HASH VARCHAR(50));
insert into TABLEA (Address, Zip, HASH) values
('ABC', '20987', 'XX'),
('XYZ', '20989', 'XY'),
('CBZ', '20980', 'XZ');
I want to get all the column names from information schema as a string seperated by comma
exactly like the below query outputs
select listagg(COLUMN_NAME, ',') from db.information_schema.columns
where table_schema = 'schema' and TABLE_NAME = 'TABLEA'
but the problem with the above query is it doesnot retain the column order meaning when I execute the query 1st time, it outputs
ZIP,HASH,ADDRESS
whereas if I execute the same query again, it outputs
ADDRESS,ZIP,HASH
The column order is not the same, I want the column order to be consistent (as in the table) every time the query is executed
UPDATE
Adding order by ORDINAL_POSITION worked
select listagg(COLUMN_NAME, ',') WITHIN GROUP (ORDER BY ORDINAL_POSITION) from db.information_schema.columns
where table_schema = 'schema' and TABLE_NAME = 'TABLEA'
you just need to ORDER BY ORDINAL_POSITION

How to compare two SQL tables and insert the difference using PowerShell

I have total of 3 SQL Server tables called Temp_Hold_Inv, Hold_Inv and Daily_Proc.
Temp_Hold_Inv contains the latest data, Hold_Inv contain not up-to-date data.
I'm just wondering how can I compare between the Temp_Hold_Inv and Hold_inv table and if there is a difference then insert only the difference to Hold_Inv, Daily_Proc tables and add value in their additional columns.
Currently here is how my tables columns look like. Also here is how it should look when there is a new user aka difference in Temp_Hold_Inv table and insert it to the other two tables.
Temp_Hold_Inv
Hold EmpId Source Exist Type
KLM KLMNOE j.smit#g.com Yes Email
Hold_Inv
Id Hold EmpId Source Type CreatedDate
1 KLM KLMNOE j.smit#g.com Email 04/15/1996
Note: Id is auto-incremented. Also there is no Exist column here. Will need to pass current date automatically.
Daily_Proc
Id Hold EmpId Source Type Operation CreatedDate Status
1 KLM KLMNOE j.smit#g.com Email ADD 04/15/1996 New
This is my PowerShell script and I'm able get the difference but not sure how to insert the difference to Hold_Inv and Daily_Proc tables.
$Server = 'ServerName'
$Database = 'DbName'
$query2 = "
SELECT 'Temp_Hold_Inv' AS SRC, T1.*
FROM (
SELECT Hold, EmpId, Source FROM Temp_Hold_Inv
EXCEPT
SELECT Hold, EmpId, Source FROM Hold_Inv
) AS T1
UNION ALL
SELECT 'Hold_Inv' AS SRC, T2.*
FROM (
SELECT Hold, EmpId, Source FROM Hold_Inv
EXCEPT
SELECT Hold, EmpId, Source FROM Temp_Hold_Inv
) AS T2
;"
$dbData = Invoke-Sqlcmd -Query $query2 -ServerInstance $Server -Database $Database
Any help or suggestion will be really appreciated.

Generate Script with UPDATE if exists else INSERT (MSSQL)

I want to create "UPDATE if exists else INSERT" statements for existing values in a table with the following pattern:
if exists (select 1 from TABLE_NAME where Ident_Column=Identifier )
update TABLE_NAME set
Column1=Value1,
Column2=Value2,
Column3=Value3,
...
where Ident_Column=Identifier
else
insert into TABLE_NAME ( Column1, Column2, Column3, ...)
select Value1, Value2, Value3, ...
go
I try to use the MSSQL Mangement Studio (2014) script generator, but I do not get the output as desired.
EDIT: The desired output IS the SQL statement, #Rusland K. & Nick.McDermaid . I want to use the script generator to generate this SQL code for each (or selected) rows of the table TABLE.
Identifier is not a variable, its a value.
If the value Identifier in Column Ident_Column exists, set the row accordingly (Value 1-n). If that Identifier is not found in any row, create a now row accordingly.
#bmsqldev: This is the entire code (just replaced the real column/table names and the concrete values)
Actually I'm don't understand what caused the missunderstanding here. If you can tell, I'd be happy to learn.
EDIT2: I endet up writing a small script which converts the script output of MSSQL Mangement Studio according to this pattern, which took me about 2 hours.
MERGE TABLE_NAME AS TARGET
USING(
SELECT
*
FROM
DBO.TABLE_NAME
WHERE
IDENT_COLUMN=IDENTIFIER
) AS SOURCE
ON(
TARGET.COLUMN1 = SOURCE.COLUMN1
AND TARGET.COLUMN2 = SOURCE.COLUMN2
AND TARGET.COLUMN3 = SOURCE.COLUMN3
)
WHEN MATCHED THEN UPDATE
SET
TARGET.COLUMN1=SOURCE.VALUE1,
TARGET.COLUMN2=SOURCE.VALUE2,
TARGET.COLUMN3=SOURCE.VALUE3,
WHEN NOT MATCHED THEN INSERT
(
COLUMN1, COLUMN1, COLUMN1, ...)
VALUES(
(VALUE1, VALUE2, VALUE3, ...
),
SOURCE.VALUE1,
SOURCE.VALUE2,
SOURCE.VALUE3,...
);;

vb.net comparing two databases then insert or delete

in vb.net how would you loop through database1 to check that all records in database2 exist in Database1 and the other way areound if a record exist in database1 and doesnt exist in database2 then delete it from database1.
so database2 is my reference
how can i do this using queries, also does it have to include nested looping?
note that the records are not in the same order
Thanks
This query will return all of the rows in the attached table that are not in the local version of the table
SELECT * FROM attachedTable
WHERE col1 NOT IN( SELECT lt.col1 FROM localTable as lt)
And this will do the converse, returning all rows in the local table that are not matched in the remote table.
SELECT * FROM localTable
WHERE col1 NOT IN( SELECT rt.col1 FROM attachedTable As rt)

What columns can be used in OUTPUT INTO clause?

I'm trying to build a mapping table to associate the IDs of new rows in a table with those that they're copied from. The OUTPUT INTO clause seems perfect for that, but it doesn't seem to behave according to the documentation.
My code:
DECLARE #Missing TABLE (SrcContentID INT PRIMARY KEY )
INSERT INTO #Missing
( SrcContentID )
SELECT cshadow.ContentID
FROM Private.Content AS cshadow
LEFT JOIN Private.Content AS cglobal ON cshadow.Tag = cglobal.Tag
WHERE cglobal.ContentID IS NULL
PRINT 'Adding new content headers'
DECLARE #Inserted TABLE (SrcContentID INT PRIMARY KEY, TgtContentID INT )
INSERT INTO Private.Content
( Tag, Description, ContentDate, DateActivate, DateDeactivate, SortOrder, CreatedOn, IsDeleted, ContentClassCode, ContentGroupID, OrgUnitID )
OUTPUT cglobal.ContentID, INSERTED.ContentID INTO #Inserted (SrcContentID, TgtContentID)
SELECT Tag, Description, ContentDate, DateActivate, DateDeactivate, SortOrder, CreatedOn, IsDeleted, ContentClassCode, ContentGroupID, NULL
FROM Private.Content AS cglobal
INNER JOIN #Missing AS m ON cglobal.ContentID = m.SrcContentID
Results in the error message:
Msg 207, Level 16, State 1, Line 34
Invalid column name 'SrcContentID'.
(line 34 being the one with the OUTPUT INTO)
Experimentation suggests that only rows that are actually present in the target of the INSERT can be selected in the OUTPUT INTO. But this contradicts the docs in the books online. The article on OUTPUT Clause has example E that describes a similar usage:
The OUTPUT INTO clause returns values
from the table being updated
(WorkOrder) and also from the Product
table. The Product table is used in
the FROM clause to specify the rows to
update.
Has anyone worked with this feature?
(In the meantime I've rewritten my code to do the job using a cursor loop, but that's ugly and I'm still curious)
You can do this with a MERGE in Sql Server 2008. Example code below:
--drop table A
create table A (a int primary key identity(1, 1))
insert into A default values
insert into A default values
delete from A where a>=3
-- insert two values into A and get the new primary keys
MERGE a USING (SELECT a FROM A) AS B(a)
ON (1 = 0) -- ignore the values, NOT MATCHED will always be true
WHEN NOT MATCHED THEN INSERT DEFAULT VALUES -- always insert here for this example
OUTPUT $action, inserted.*, deleted.*, B.a; -- show the new primary key and source data
Result is
INSERT, 3, NULL, 1
INSERT, 4, NULL, 2
i.e. for each row the new primary key (3, 4) and the old one (1, 2). Creating a table called e.g. #OUTPUT and adding " INTO #OUTPUT;" at the end of the OUTPUT clause would save the records.
I've verified that the problem is that you can only use INSERTED columns. The documentation seems to indicate that you can use from_table_name, but I can't seem to get it to work (The multi-part identifier "m.ContentID" could not be bound.):
TRUNCATE TABLE main
SELECT *
FROM incoming
SELECT *
FROM main
DECLARE #Missing TABLE (ContentID INT PRIMARY KEY)
INSERT INTO #Missing(ContentID)
SELECT incoming.ContentID
FROM incoming
LEFT JOIN main
ON main.ContentID = incoming.ContentID
WHERE main.ContentID IS NULL
SELECT *
FROM #Missing
DECLARE #Inserted TABLE (ContentID INT PRIMARY KEY, [Content] varchar(50))
INSERT INTO main(ContentID, [Content])
OUTPUT INSERTED.ContentID /* incoming doesn't work, m doesn't work */, INSERTED.[Content] INTO #Inserted (ContentID, [Content])
SELECT incoming.ContentID, incoming.[Content]
FROM incoming
INNER JOIN #Missing AS m
ON m.ContentID = incoming.ContentID
SELECT *
FROM #Inserted
SELECT *
FROM incoming
SELECT *
FROM main
Apparently the from_table_name prefix is only allowed on DELETE or UPDATE (or MERGE in 2008) - I'm not sure why:
from_table_name
Is a column prefix that specifies a table included in the FROM clause of a DELETE or UPDATE statement that is used to specify the rows to update or delete.
If the table being modified is also specified in the FROM clause, any reference to columns in that table must be qualified with the INSERTED or DELETED prefix.
I'm running into EXACTLY the same problem as you are, I feel your pain...
As far as I've been able to find out there's no way to use the from_table_name prefix with an INSERT statement.
I'm sure there's a viable technical reason for this, and I'd love to know exactly what it is.
Ok, found it, here's a forum post on why it doesn't work:
MSDN forums
I think I found a solution to this problem, it sadly involves a temporary table, but at least it'll prevent the creation of a dreaded cursor :)
What you need to do is add an extra column to the table you're duplicating records from and give it a 'uniqueidentifer' type.
then declare a temporary table:
DECLARE #tmptable TABLE (uniqueid uniqueidentifier, original_id int, new_id int)
insert the the data into your temp table like this:
insert into #tmptable
(uniqueid,original_id,new_id)
select NewId(),id,0 from OriginalTable
the go ahead and do the real insert into the original table:
insert into OriginalTable
(uniqueid)
select uniqueid from #tmptable
Now to add the newly created identity values to your temp table:
update #tmptable
set new_id = o.id
from OriginalTable o inner join #tmptable tmp on tmp.uniqueid = o.uniqueid
Now you have a lookup table that holds the new id and original id in one record, for your using pleasure :)
I hope this helps somebody...
(MS) If the table being modified is also specified in the FROM clause, any reference to columns in that table must be qualified with the INSERTED or DELETED prefix.
In your example, you can't use cglobal table in the OUTPUT unless it's INSERTED.column_name or DELETED.column_name:
INSERT INTO Private.Content
(Tag)
OUTPUT cglobal.ContentID, INSERTED.ContentID
INTO #Inserted (SrcContentID, TgtContentID)
SELECT Tag
FROM Private.Content AS cglobal
INNER JOIN #Missing AS m ON cglobal.ContentID = m.SrcContentID
What worked for me was a simple alias table, like this:
INSERT INTO con1
(Tag)
OUTPUT **con2**.ContentID, INSERTED.ContentID
INTO #Inserted (SrcContentID, TgtContentID)
SELECT Tag
FROM Private.Content con1
**INNER JOIN Private.Content con2 ON con1.id=con2.id**
INNER JOIN #Missing AS m ON con1.ContentID = m.SrcContentID

Resources