Record should only be loaded to target on a scenario - sql

I have two tables a stage table and a target table. I want my target table to hold valid CustomerScore values. Currently, we insert into staging and load to our target table. We do not want to load invalid values(-8.0000). However, if there is a customerNumber with a valid value in our target table we would like to decommission numbers by giving it a customerScore of (-8.0000). This should be the only time this value makes it into the target table, so a record for that CustomerNumber has to already be in the target for this to update that record currently in the target table. My create statement is below
CREATE TABLE stg.CustomerAppreciation (
CustomerId INT identity(1, 1)
,CustomerNumber VARCHAR(50)
,CustomerScore DECIMAL(5, 4)
);
CREATE TABLE ods.CustomerAppreciation (
CustomerId INT identity(1, 1)
,CustomerNumber VARCHAR(50)
,CustomerScore DECIMAL(5, 4)
);
Currently, my target table has two records, each value below belongs to my create table fields.
1 123 0.8468
2 143 1.0342
Now say we want to decommission CustomerID = 2 because there is a record been inserted into staging as
3 143 -8.0000
The target table should now be updated on this CustomerNumber. Making my target table look like:
1 123 0.8468
2 143 -8.0000
This should be the only time we allow -8.0000 into the table when a CustomerNumber already exists. If a customerNumber does not exists in the target table and for some reason -8.0000 is seen in staging it should not be allowed in. How would I write an update query that updates a record in my target table only if that scenario exists and prevents -8.0000 from coming in if it does not exist?

Assuming the staging table only contains one row per customer number (if not, group it to show the highest customer Id), you can use a merge to perform this function. Without checking exact syntax, something like this:
MERGE ods.CustomerAppreciation AS Target
USING (SELECT * FROM stg.CustomerAppreciation WHERE CustomerScore >= 0) AS Source ON Target.CustomerNumber = Source.CustomerNumber
WHEN MATCHED
-- choose your match criteria here
--AND Source.CustomerId > Target.CustomerId
AND NOT EXISTS (SELECT Target.* INTERSECT SELECT Source.*)
THEN UPDATE
SET Target.CustomerScore = Source.CustomerScore;

Not sure if I fully understand the specifics but here is some syntax that should help to at least get you started ...
BEGIN;
MERGE ods.CustomerAppreciation AS X
USING (SELECT CustomerNumber,CustomerScore FROM stg.CustomerAppreciation) AS Y (CustomerNumber,CustomerScore)
ON (X.CustomerNumber = Y.CustomerNumber)
WHEN MATCHED /*AND Y.CustomerNumber = '-8.0000'*/ THEN
UPDATE SET CustomerScore = Y.CustomerScore
WHEN NOT MATCHED BY X /*AND Y.CustomerNumber = '-8.0000'*/ THEN
INSERT (CustomerNumber,CustomerScore)
VALUES (Y.CustomerNumber,Y.CustomerScore)
OUTPUT $action, inserted.* INTO #MyTempTable;
END;

Related

Create a record in table A and assign its id to table B

I have a set of companies and for each of them, I need to generate a UUID in another table.
companies table
detail_id (currently NULL for all companies)
details table
id
uuid
date
...
I'd like to update all companies with newly created detail like this:
UPDATE companies
SET details_id =
(
INSERT INTO details
(uuid, date)
VALUES (uuid_generate_v1(), now()::date)
RETURNING id
)
But that gives me a syntax error since I can't use INSERT INTO inside UPDATE.
What is a proper way to create a row in the details table and immediately set the newly created id in the companies table?
(I am using Postgres)
You can use a data-modifying CTE:
with new_rows as (
INSERT INTO details ("uuid", "date")
VALUES (uuid_generate_v1(), current_date)
RETURNING id
)
update companies
set details_id = new_rows.id
from new_rows

Inserting an ID that increments (but is not an AUTOINCREMENT)

I have a table that I am trying to insert multiple records into using a select statement.
The ID field is an INT and not autoincremented but I do need to increment in in the INSERT.
The table belongs to a third party product we use for our ERP so I cannot change the property of the ID.
The insert is supposed to create a record in the EXT01100 table for each line item on a particular sales order.
Here is the code I am using:
INSERT INTO EXT01100 (Extender_Record_ID, Extender_Window_ID, Extender_Key_Values_1 , Extender_Key_Values_2, Extender_Key_Values_3)
SELECT (SELECT MAX(EXTENDER_RECORD_ID) + 1 FROM EXT01100), 'ECO_FEE_DIGIT', SOL.LNITMSEQ, SOL.SOPNUMBE, SOL.SOPTYPE
FROM SOP10200 SOL WITH(NOLOCK)
WHERE SOL.SOPTYPE = #InTYPE AND SOL.SOPNUMBE = #INNUMBE AND SOL.LNITMSEQ <> 0
This works on a single line order, but multiple line orders will produce a Primary Key duplicate error so I don't think I can use (SELECT MAX(EXTENDER_RECORD_ID) + 1 FROM EXT01100) in this case.
This is in SQL server.
Any help is greatly appreciated!
You can use row_number to ensure each row has a unique ID, and you need to take an exclusive lock on your main sequence table, and you need to remove your nolock.
INSERT INTO EXT01100 (Extender_Record_ID, Extender_Window_ID, Extender_Key_Values_1 , Extender_Key_Values_2, Extender_Key_Values_3)
SELECT (SELECT MAX(EXTENDER_RECORD_ID) FROM EXT01100 WITH (TABLOCKX)) + ROW_NUMBER() OVER (ORDER BY SOL.LNITMSEQ)
, 'ECO_FEE_DIGIT', SOL.LNITMSEQ, SOL.SOPNUMBE, SOL.SOPTYPE
FROM SOP10200 SOL
WHERE SOL.SOPTYPE = #InTYPE AND SOL.SOPNUMBE = #INNUMBE AND SOL.LNITMSEQ <> 0;
Seconding a recommendation from the comments above, we use Sequences in our production system with no problem. Here's how it looks:
create sequence SQ_Extender_Record_ID
minvalue 1
start with 1
cache 100;
INSERT INTO EXT01100 (Extender_Record_ID, Extender_Window_ID, Extender_Key_Values_1 , Extender_Key_Values_2, Extender_Key_Values_3)
SELECT (next value for SQ_Extender_Record_ID), 'ECO_FEE_DIGIT', SOL.LNITMSEQ, SOL.SOPNUMBE, SOL.SOPTYPE
FROM SOP10200 SOL
WHERE SOL.SOPTYPE = #InTYPE AND SOL.SOPNUMBE = #INNUMBE AND SOL.LNITMSEQ <> 0
Obviously, adjust the min/start values as appropriate for your situation.
If you want, you could add a default constraint to the table/column with this:
alter table EXT01100 add constraint DF_EXT01100__Extender_Record_ID
default (next value for SQ_Extender_Record_ID)
for Extender_Record_ID
You mention that this is in a database whose schema you don't control, so that may not be an option; I mention it for the sake of completeness.

How to use trigger to update column for newly inserted tuple

I have a table BillRecords
bill_id buy_amt sell_amt profit
----------------------------------------------------
0 200 300 NULL
1 1000 1200 NULL
Let's say I want to insert following record
INSERT INTO BillRecords(bill_id, buy_amt, sell_amt)
VALUES (2, 2000, 2500)
I'm expecting that as soon as I insert record, trigger should only update profit column for newly inserted record (i.e. WHERE bill_id = 2) and not for all the rows in profit column. What should I do?
Note: I'm using SQL Server
No need for a trigger to do what you want. It is much simpler, and more efficient, to use a computed column:
create table billrecords (
bill_id int primary key,
buy_amt int,
sell_amt int,
profit as (buy_amt - sell_amt)
);
Or if you want to add it to an already-existing table:
alter table billrecords
add profit as (buy_amt - sell_amt);
This gives you an always up-to-date value, that you can access just like any other column. For better performance, you can use option persisted, so the value column is physically stored, and recomputed only when needed instead of every time it is accessed.
Since you only want to update the profit amount of the transaction having bill_id = 2, you can use the below after trigger this in combination with a derived column "profit" which fetches the profit amount.
CREATE TRIGGER trigger_name
ON billrecords
After INSERT
AS
IF EXISTS (SELECT * from inserted)
begin
update m
set m.profit = m.sell_amt - m.bill_amt, m.bill_amt = null, m.sell_amt = null
from billrecords m join inserted i
on i.bill_id = m.bill_id
where i.bill_id = 2
end;

sql conversion script

I have a 2 databases that I want to merge with some similiar tables. The source tables have id as bigint and my destination table has int as ID. There aren't that many records in my source table (< 20k) so I want to assign new ids to all records so the ids can fit in an int. How can I do this with sql?
First Option
You can Use Sequence object as follow:
First Create a Sequence object and assign it's Start With value to max Id value in destination table plus 1. For example if max Id in destination table is 100, you need to assign 101 as Start With. You can also obtain the max Id value from destination table using a Max(Id) aggregate function and store it in a variable:
CREATE SEQUENCE SeqId
START WITH [Max value of Id in destination table]
INCREMENT BY 1 ;
GO
Then insert to destination table using following query:
Insert Into tblXXX (Id, ...) Values (NEXT VALUE FOR SeqId, ...)
Read more about Sequence Object
Second Option
You can make the destination table's Id column as Identity column with seed equal to destination table's Id column max value and Increment equal to 1.
Here is detailed example also Here
You did not provide much details so I can only provide a general guideline:
Note: Example assumes that you want to merge tables A and B into C and you want to generate new IDs. I also assume that these IDs are not referenced by other tables (foreign keys).
First you get record counts from tables A and B:
DECLARE #countA INT
DECLARE #countB INT
SET #countA = ( SELECT COUNT(*) FROM A )
SET #countB = ( SELECT COUNT(*) FROM B )
Next you use a window function to generate new IDs and insert records into table C.
INSERT INTO C
SELECT #countA + ROW_NUMBER() OVER( ORDER BY ID ) AS ID, ....
FROM A
INSERT INTO C
SELECT #countA + #countB + ROW_NUMBER() OVER( ORDER BY ID ) AS ID, ....
FROM B

Insert data from one table to other using select statement and avoid duplicate data

Database: Oracle
I want to insert data from table 1 to table 2 but the catch is, primary key of table 2 is the combination of first 4 letters and last 4 numbers of the primary key of table 1.
For example:
Table 1 - primary key : abcd12349887/abcd22339887/abcder019987
In this case even if the primary key of table 1 is different, but when I extract the 1st 4 and last 4 chars, the output will be same abcd9887
So, when I use select to insert data, I get error of duplicate PK in table 2.
What I want is if the data of the PK is already present then don't add that record.
Here's my complete stored procedure:
INSERT INTO CPIPRODUCTFAMILIE
(productfamilieid, rapport, mesh, mesh_uitbreiding, productlabelid)
(SELECT DISTINCT (CONCAT(SUBSTR(p.productnummer,1,4),SUBSTR(p.productnummer,8,4)))
productnummer,
ps.rapport, ps.mesh, ps.mesh_uitbreiding, ps.productlabelid
FROM productspecificatie ps, productgroep pg,
product p left join cpiproductfamilie cpf
on (CONCAT(SUBSTR(p.productnummer,1,4),SUBSTR(p.productnummer,8,4))) = cpf.productfamilieid
WHERE p.productnummer = ps.productnummer
AND p.productgroepid = pg.productgroepid
AND cpf.productfamilieid IS NULL
AND pg.productietype = 'P'
**AND p.ROWID IN (SELECT MAX(ROWID) FROM product
GROUP BY (CONCAT(SUBSTR(productnummer,1,4),SUBSTR(productnummer,8,4))))**
AND (CONCAT(SUBSTR(p.productnummer,1,2),SUBSTR(p.productnummer,8,4))) not in
(select productfamilieid from cpiproductfamilie));
The highlighted section seems to be wrong, and because of this the data is not picking up.
Please help
Try using this.
p.productnummer IN (SELECT MAX(productnummer) FROM product
GROUP BY (CONCAT(SUBSTR(productnummer,1,4),SUBSTR(productnummer,8,4))))