SQL: Update and Insert with condition - sql

I would like to know if this is possible. I would like to update a record only if the typeId equal my value and add a record in table B if that's the case.
TableA:
id (PK, int)
typeId (int)
TableB:
id (PK, int)
tableAId (FK, int)
note (nvarchar)
My SQL script:
UPDATE [dbo].[TableA]
SET [TypeId] = CASE
WHEN [TypeId] = 4 THEN 6 AND
(INSERT INTO [dbo].[TableB] ([tableAId],[note])
VALUES ([dbo].[TableA].Id,'type has changed'))
ELSE [Id]
END
The script above looks like I want to achieve but obviously it's incorrect. How can I do multiple things in my case condition? Update a value and insert a record with the current id?
Data sample:
Table A (id, typeId)
1, 4
2, 5
3, 2
Table B (id, tableAid, note)
1, 1, 'note1'
2, 1, 'note2'
3, 2, 'note1'
Should become:
Table A (id, typeId)
1, 6
2, 5
3, 2
Table B (id, tableAid, note)
1, 1, 'note1'
2, 1, 'note2'
3, 2, 'note1'
4, 1, 'type has changed'

Try to use OUTPUT clause.
Test tables and data:
CREATE TABLE A(
id int NOT NULL PRIMARY KEY,
typeId int NOT NULL
)
INSERT A(id,typeId)VALUES(1, 4),(2, 5),(3, 2)
CREATE TABLE B(
id int NOT NULL IDENTITY PRIMARY KEY,
tableAid int NOT NULL REFERENCES A(id),
note varchar(50) NOT NULL
)
SET IDENTITY_INSERT B ON
INSERT B(id,tableAid,note)VALUES(1, 1, 'note1'),(2, 1, 'note2'),(3, 2, 'note1')
SET IDENTITY_INSERT B OFF
Using OUTPUT demo:
DECLARE #LogTable TABLE(tableAid int,note varchar(50))
UPDATE A
SET
typeId=6
OUTPUT inserted.id,'type has changed'
INTO #LogTable(tableAid,note)
WHERE typeID=4
INSERT B(tableAid,note)
SELECT tableAid,note
FROM #LogTable
If you drop the foreign key in the table B then you can use OUTPUT directly into table B without #LogTable:
-- table B without FOREIGN KEY
CREATE TABLE B(
id int NOT NULL IDENTITY PRIMARY KEY,
tableAid int NOT NULL, --REFERENCES A(id),
note varchar(50) NOT NULL
)
-- only one UPDATE with OUTPUT
UPDATE A
SET
typeId=6
OUTPUT inserted.id,'type has changed'
INTO B(tableAid,note)
WHERE typeID=4

Related

Create new records in one table-A and add as foreign key to table-B if the foreign key field in table-B is null -PostgresSQL

Table A and Table B look like shown below. The intention is to write an SQL script to run and update the tables. Table B has a foreign key to Table-A. If the field is null in Table B, then create a new record in Table-A and update Table B with that foreign key
It is expected to add two new records in Table A and add those foreign key in the Table B as per the above example. Thanks in advance
I wrote for you sample for do it.
But I don't know your business logic detail. You can change some solutions.
CREATE TABLE tablea (
id serial4 NOT NULL,
"name" varchar NULL,
CONSTRAINT tablea_pk PRIMARY KEY (id)
);
CREATE TABLE tableb (
id serial4 NOT NULL,
a_id int4 NULL,
"name" varchar NULL,
CONSTRAINT tableb_pk PRIMARY KEY (id)
);
INSERT INTO tableb (id, a_id, "name") VALUES(1, 100, 'b1');
INSERT INTO tableb (id, a_id, "name") VALUES(2, NULL, 'b2');
INSERT INTO tableb (id, a_id, "name") VALUES(3, NULL, 'b3');
INSERT INTO tableb (id, a_id, "name") VALUES(4, NULL, 'b4');
-- create function for inserting data into tablea and returning these id
CREATE OR REPLACE FUNCTION tablea_inserting()
RETURNS integer
LANGUAGE plpgsql
AS $function$
declare
ret int4;
begin
insert into tablea ("name") values ('test')
returning id into ret;
return ret;
end
$function$
;
-- After then you can update your tableb
update tableb set
a_id = tablea_inserting()
where a_id is null
select * from tableb;
Result:
id a_id name
1 100 b1
2 1 b2
3 2 b3
4 3 b4

Trigger on status change

I need to write a trigger in SQL, first of all I show you my table structure
CREATE TABLE ZAMOW
(
IDZAMOW int primary key identity(1,1) not null,
IDKLIENTA int not null REFERENCES KLIENT(IDKLIENTA),
DATA DATE not null DEFAULT(GETDATE()),
STATUS char(1) CHECK(STATUS = 'P' OR STATUS = 'W' OR STATUS = 'Z' OR STATUS = 'A' )DEFAULT('P')
)
CREATE TABLE ZAMOCZESCI
(
IDZAMOW int not null REFERENCES ZAMOW(IDZAMOW),
IDCZESCI int not null REFERENCES CZESC(IDCZESCI),
ILOSC float not null
)
CREATE TABLE CZESC
(
IDCZESCI int primary key identity(1,1) not null,
NAZWA char(30) not null CHECK ((datalength([NAZWA])>(3))),
OPIS char(200) DEFAULT('Brak opisu'),
CENA decimal(10,2) not null
)
CREATE TABLE MAGACZESCI
(
IDMAGAZYNU int not null REFERENCES MAGAZYN(IDMAGAZYNU),
IDCZESCI int not null REFERENCES CZESC(IDCZESCI),
ILOSC float not null
)
I want to create a trigger that will trigger only if ZAMOW.STATUS changes to 'W' or 'Z' and then will subtract all values MAGACZESCI.ILOSC = MAGACZESCI.ILOSC - ZAMOCZESCI.ILOSC identifying id by IDCZESCI
For example if I have in table MAGACZESCI values (1,1,5) and (1,2,5) // 5 pieces of part number 1 and 2, in table ZAMOW(1,1,currentdate,'P'),
and in table ZAMOCZESCI (1,1,3), (1,2,2) 3 pieces of part 1 and 2 pieces of part 2
I want to trigger only if status changes from 'P' -> 'W' OR 'Z'
and then to change values in MAGACZESCI to (1,1,5-3) and (1,2,5-2) identifying by IDCZESCI
This example is for 2 rows but I want it to be more flexible, sometimes even for 100 or more rows
I came up with something like this
CREATE TRIGGER STATUSCHANGE
ON ZAMOW
AFTER UPDATE
AS
IF UPDATE(STATUS)
IF (ZAMOW.STATUS = 'Z' OR ZAMOW.STATUS = 'W')
DECLARE #idczesci int
DECLARE #ilosc float
DECLARE C1 CURSOR FOR SELECT ZAMOCZESCI.IDCZESCI,ZAMOCZESCI.ILOSC FROM ZAMOCZESCI WHERE ZAMOCZESCI.IDZAMOW = ZAMOW.IDZAMOW
OPEN C1
FETCH NEXT FROM C1 INTO #idczesci,#ilosc
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE MAGACZESCI
SET ILOSC = ILOSC - #ilosc
WHERE IDCZESCI = #idczesci
END
GO
But I don't know how to tell SQL that IF (ZAMOW.STATUS = 'Z' OR ZAMOW.STATUS = 'W') id for rows that are updated, it tells me couldn't bound because of multipart identifier.
This is incomplete as in your attempt you reference the columns that don't exist in the table ZAMOW yet that is what the trigger is on. I suspect that is because we have incomplete DDL as you have keys on tables like MAGAZYN that don't exist in the DDL you provide.
Anyway, this should be enough to get you where you need to be, however, I can't test it, as the columns ILOSC and idczesci don't exist in your sample DDL:
CREATE TRIGGER trg_StatusChange ON dbo.ZAMOW
AFTER UPDATE AS
BEGIN
IF UPDATE ([Status]) BEGIN
UPDATE M
SET ILOSC = ILOSC - i.ILOSC --This column doesn't exist in your table, so where is it coming from?
FROM dbo.MAGACZESCI M
JOIN inserted i ON M.IDCZESCI = i.idczesci --This column doesn't exist in your table, so where is it coming from?
JOIN deleted d ON i.IDZAMOW = d.IDZAMOW --This column doesn't exist in your table, so where is it coming from?
WHERE d.[STATUS] = 'P'
AND i.[status] != 'P';
END;
END;
..just a try...maybe you get something out of it..
drop table if exists ZAMOCZESCI_test
go
drop table if exists MAGACZESCI_test
go
drop table if exists ZAMOW_test
go
drop table if exists CZESC_test
go
CREATE TABLE ZAMOW_test
(
IDZAMOW int primary key identity(1,1) not null,
IDKLIENTA int not null, -- REFERENCES KLIENT(IDKLIENTA),
DATA DATE not null DEFAULT(GETDATE()),
STATUS char(1) CHECK(STATUS = 'P' OR STATUS = 'W' OR STATUS = 'Z' OR STATUS = 'A' )DEFAULT('P')
)
go
insert into ZAMOW_test(IDKLIENTA)
values(510), (520),(530);
go
CREATE TABLE CZESC_test
(
IDCZESCI int primary key identity(1,1) not null,
NAZWA char(30) not null CHECK ((datalength([NAZWA])>(3))),
OPIS char(200) DEFAULT('Brak opisu'),
CENA decimal(10,2) not null
)
go
insert into CZESC_test(NAZWA, CENA)
values('A123', 4), ('B567', 3), ('C009', 7),
--
('X001', 150), ('X002', 500), ('X003', 700)
;
go
CREATE TABLE ZAMOCZESCI_test
(
IDZAMOW int not null REFERENCES ZAMOW_test(IDZAMOW),
IDCZESCI int not null REFERENCES CZESC_test(IDCZESCI),
ILOSC float not null
)
go
insert into ZAMOCZESCI_test(IDZAMOW, IDCZESCI, ILOSC)
values(1, 1, 3), (1, 2, 2), (1, 3, 9),
--2nd&3rd clients
(2, 4, 60), (2, 5, 100), (2, 6, 300),
(3, 5, 150), (3, 6, 120);
go
CREATE TABLE MAGACZESCI_test
(
IDMAGAZYNU int not null,-- REFERENCES MAGAZYN(IDMAGAZYNU),
IDCZESCI int not null REFERENCES CZESC_test(IDCZESCI),
ILOSC float not null
)
go
insert into MAGACZESCI_test(IDMAGAZYNU, IDCZESCI, ILOSC)
values (1001, 1, 5), (1002, 2, 5), (1003, 3, 20),
--
(1004, 4, 200), (1005, 5, 1000), (1006, 6, 2000);
--client 2, assigned 60units of prod4, after trigger: prod4 = 200-60=140
--clients 2&3, assigned 100+150 units of prod5, after trigger: prod5 = 1000 - (100+150) = 750
-- prod6, after trigger: prod6 = 2000 - (300+120) = 1580
go
--based on this
/*
select *
from ZAMOW_test as zam --<--(is replaced by deleted&inserted)
join ZAMOCZESCI_test as zamcze on zam.IDZAMOW = zamcze.IDZAMOW --but this needs to get grouped by IDCZESCI, if multiple "orders" change status in a single update
join MAGACZESCI_test as mag on zamcze.IDCZESCI = mag.IDCZESCI
*/
go
create trigger trgUpdateZAMOW_test on ZAMOW_test for /*after*/ update
as
begin
--set nocount on
--set rowcount 0
--if status did not change from p->w or z, do nothing, return/exit
if not exists
(
select *
from deleted as d
join inserted as i on d.IDZAMOW = i.IDZAMOW
where d.STATUS = 'P'
and i.STATUS in ('W', 'Z')
)
begin
return;
end
--get the "orders" which changed from p->w||z, aggregate their products and subtract the product sums from "inventory"?
update mag
set ILOSC = mag.ILOSC - updcze.sumILOSC --what happens if mag.ILOSC - updcze.sumILOSC < 0 ??
--output deleted.*, inserted.*
from
(
select zamcze.IDCZESCI, isnull(sum(zamcze.ILOSC), 0) as sumILOSC
from deleted as d
join inserted as i on d.IDZAMOW = i.IDZAMOW
join ZAMOCZESCI_test as zamcze on d.IDZAMOW = zamcze.IDZAMOW
where d.STATUS = 'P'
and i.STATUS in ('W', 'Z')
group by zamcze.IDCZESCI
having isnull(sum(zamcze.ILOSC), 0) <> 0
) as updcze
join MAGACZESCI_test as mag on updcze.IDCZESCI = mag.IDCZESCI;
end
go
update ZAMOW_test
set STATUS = 'A' --trigger fires and returns/exits, x->A
go
update ZAMOW_test
set STATUS = 'W' --trigger fires and returns/exits, status:from A->W
go
update ZAMOW_test
set STATUS = 'P' --trigger fires, exits, status:from W->P
go
select 'before', *
from MAGACZESCI_test
update ZAMOW_test
set STATUS = 'W' --trigger fires , and updates mag, status: from P->W
select 'after', *
from MAGACZESCI_test;

How to insert values in table if there is a primary key on id field in it?

I have trouble, I have table named my_table)
id name
1 A
2 B
3 C
and I want to insert top 2 so, values have autoincrement automaticaly like this
insert into my_table (name) values (A), (B);
Is this possible in postgressql?
May be I should have count() + 1, then count() + 2 here
insert into my_table (id, name) values (count(*) + 1, A), (count(*) + 2, B);
or something like this
because my id has constraint
BIGSEREIAL PRIMARY KEY NOT NULL
And I cant add values without getting the last id in table.
You can do:
create table my_table (
id int not null generated always as identity,
name varchar(10) not null
);
insert into my_table (name) values ('A'), ('B');
Then:
select * from my_table;
Result:
id name
-- ----
1 A
2 B
See running example at db<>fiddle.

How to update a column based on matching ID's

I create two tables housing_listing and buyer. How to update the value of sold in housing_listing table to TRUE if the id (transaction_id) of housing_listing matches the id (transaction_id) of buyer?
Creating tables:
CREATE TABLE housing_listing (
transaction_id INT PRIMARY KEY,
number_of_bedrooms INT,
number_of_bathrooms INT,
listing_price INT,
listing_agent TEXT,
agent_email TEXT,
listing_office TEXT,
date_of_listing DATETIME,
zip_code INT,
sold BOOL,
Commission INT
);
CREATE TABLE buyer (
transaction_id INT PRIMARY KEY,
buyer_name TEXT,
buyer_id INT,
sale_price INT,
date_of_sale DATETIME,
selling_agent TEXT
)
Inserting data to buyer table:
INSERT INTO buyer VALUES (1, "Ania Kraszka", 1, 2000000,'2020/02/27','FADU');
INSERT INTO buyer VALUES (2, "Ania Kraszka", 2, 2000000,'2011/02/27','FADU');
Inserting data to housing_listing table:
INSERT INTO housing_listing VALUES (1, 3, 2, 2000000, 'Liza','liza#uba.ar', 'UBA','2018/02/27',45049, 'FALSE',0);
INSERT INTO housing_listing VALUES (2, 2, 1, 3000, 'Tom','tom#utn.ar', 'UTN','2011/02/27',45049,'FALSE',0);
INSERT INTO housing_listing VALUES (9, 1, 1, 40000, 'Tom','tom#fadu.ar', 'FADU','2011/02/27',45049, 'FALSE',0);
You can use a correlated subquery:
update housing_listing
set sold = true
where exists (select 1
from buyer b
where b.transaction_id = housing_listing.transaction_id
);
I assume you mean that the transaction ids match.
You can do something like this-
UPDATE housing_listing
SET sold = 'TRUE'
WHERE transaction_id IN (SELECT transaction_id FROM buyer);
SELECT * FROM housing_listing;
1|3|2|2000000|Liza|liza#uba.ar|UBA|2018/02/27|45049|TRUE|0
2|2|1|3000|Tom|tom#utn.ar|UTN|2011/02/27|45049|TRUE|0
9|1|1|40000|Tom|tom#fadu.ar|FADU|2011/02/27|45049|FALSE|0

INSERT INTO table_name VALUES (value1, value2, value3, ...);How to transfer a data of a single column in a single table row

Hi guys I'm working on a vanilla system where we can add many systems in it with data sets and fields. we can add fields and data of each system at run time without changing the structure of database.
CREATE TABLE Type(
ID int,
Name varchar(255),
PRIMARY KEY (ID)
);
INSERT INTO Type VALUES (1, 'cust_obj_7');
INSERT INTO Type VALUES (2, 'cust_obj_8');
CREATE TABLE Object(
ID int,
Name varchar(255),
Description varchar(255),
TypeID int,
PRIMARY KEY (ID),
FOREIGN KEY (TypeID ) REFERENCES Type(ID)
);
INSERT INTO Object VALUES (1, 'First', 'First_desc', 1);
INSERT INTO Object VALUES (2, 'Second', 'Second_desc', 1);
CREATE TABLE TypeFields(
ID int,
Name varchar(255),
NameType varchar(255),
TypeID int,
PRIMARY KEY (ID),
FOREIGN KEY (TypeID ) REFERENCES Type(ID)
);
INSERT INTO TypeFields VALUES (1, 'First', 'str', 1);
INSERT INTO TypeFields VALUES (2, 'Seond', 'str', 1);
INSERT INTO TypeFields VALUES (3, 'Third', 'int', 1);
CREATE TABLE FieldsData(
ID int,
ObjectID int,
FieldID int,
FieldName varchar(255),
TypeID int,
value varchar(255),
PRIMARY KEY (ID),
FOREIGN KEY (TypeID ) REFERENCES Type(ID),
FOREIGN KEY (ObjectID ) REFERENCES Object(ID),
FOREIGN KEY (FieldID ) REFERENCES TypeFields(ID)
);
INSERT INTO VALUES (1, 1, 1, 'First', 1, "a");
INSERT INTO VALUES (2, 1, 2, 'Second', 1, "b");
INSERT INTO VALUES (3, 1, 3, 'Third', 1, "120");
INSERT INTO VALUES (4, 2, 1, 'First', 1, "c");
INSERT INTO VALUES (5, 2, 2, 'Second', 1, "d");
INSERT INTO VALUES (6, 2, 3, 'Third', 1, "130");
CREATE TABLE FinalTable(
ObjID int,
ObjName varchar(255),
ObjDesc varchar(255),
First varchar(255),
Second varchar(255),
Third int
);
Data will look like this
Insert Into FinalTable
select *
from object, fieldsdata
where object.iD = fieldsdata.objectiD
Values(object.ID , object.Name, object.Description, ....... );
I want to create a single data set form these tables as finaltable as shown above. I can get all the data using keys but I'm having issue to write that data to a single row from a column of different rows. I'm stuck that how can I achieve this using sql in sas-base.
First, "My custom table" should be a view originating from "Fields data". "Fields data" is a bit of a misnomer because it contains the values of the fields. So in an abstraction that distinction is important for understanding within the framework you are building.
A row in "My custom table" appears to be for projecting TypeID=1. You don't show other TypeID values, but I presume they would be for different custom tables.
The custom tables should really be views, otherwise each object is a potential +1 to a multiplier regarding storage requirements
In your sample image the second row of "My custom table" has ObjID=2, yet shows values from the "Fields data" that correspond to ObjID=2. I will presume a typo.
The "Object types" name is not rendered in the Custom table, thus I would consider it simply a catalog of types.
The design you present is not a normal form yet. Not sure why FieldName is replicated in "Fields data", you have the FieldID that refers to a record with the name. Not sure why TypeID is present in "Fields data" because TypeID is essentially a catalog item selector for the desired projection of the "Fields data"
This kind of design can cause a lot of reinvention and will take huge amounts of time to flesh out for different value types such as dates, value rendering formats, multivalued types, etc...
Regardless, the projection of the values data as an object type to a custom table is essentially a transposition of a join that combines object, type fields, and fields data. SAS SQL does not have a PIVOT operator (such as is found in MS SQL Server). The 'old school' way to pivot in SQL involves aggregating case statements over a by group. Search SO
There are some designs for an 'arbitrarium' that simply kill all the middle men and have a single enormous values monolith with say 20 ID fields, 1,000 numeric fields, 1,000 character fields and 100 date fields and each 'object' is a use-case SQL view against it.
** EDIT - ADDED BELOW **
Added code demonstrating 'old school' pivot:
PROC SQL;
CREATE TABLE Type(
ID int,
Name varchar(255)/*,
PRIMARY KEY (ID)*/
);
INSERT INTO Type VALUES (1, 'cust_obj_7');
INSERT INTO Type VALUES (2, 'cust_obj_8');
CREATE TABLE Object(
ID int,
Name varchar(255),
Description varchar(255),
TypeID int/*,
PRIMARY KEY (ID),
FOREIGN KEY (TypeID ) REFERENCES Type(ID);*/
);
INSERT INTO Object VALUES (1, 'First', 'First_desc', 1);
INSERT INTO Object VALUES (2, 'Second', 'Second_desc', 1);
CREATE TABLE TypeFields(
ID int,
Name varchar(255),
NameType varchar(255),
TypeID int/*,
PRIMARY KEY (ID),
FOREIGN KEY (TypeID ) REFERENCES Type(ID) */
);
INSERT INTO TypeFields VALUES (1, 'First', 'str', 1);
INSERT INTO TypeFields VALUES (2, 'Seond', 'str', 1);
INSERT INTO TypeFields VALUES (3, 'Third', 'int', 1);
CREATE TABLE FieldsData(
ID int,
ObjectID int,
FieldID int,
FieldName varchar(255),
TypeID int,
value varchar(255)/*,
PRIMARY KEY (ID),
FOREIGN KEY (TypeID ) REFERENCES Type(ID),
FOREIGN KEY (ObjectID ) REFERENCES Object(ID),
FOREIGN KEY (FieldID ) REFERENCES TypeFields(ID)*/
);
INSERT INTO FieldsData VALUES (1, 1, 1, 'First', 1, "a");
INSERT INTO FieldsData VALUES (2, 1, 2, 'Second', 1, "b");
INSERT INTO FieldsData VALUES (3, 1, 3, 'Third', 1, "120");
INSERT INTO FieldsData VALUES (4, 2, 1, 'First', 1, "c");
INSERT INTO FieldsData VALUES (5, 2, 2, 'Second', 1, "d");
INSERT INTO FieldsData VALUES (6, 2, 3, 'Third', 1, "130");
CREATE TABLE FinalTable(
ObjID int PRIMARY KEY,
ObjName varchar(255),
ObjDesc varchar(255),
First varchar(255),
Second varchar(255),
Third int
);
create view example_type1_realized as
select
FieldsData.ObjectID as ObjID
, max(Object.Name) as ObjName
, max(Object.Description) as ObjDesc
, max(case when FieldID=1 then Value end) as First
, max(case when FieldID=2 then Value end) as Second
, max(case when FieldID=3 then input(Value,best12.) end) as Third
from FieldsData
join Object
on FieldsData.ObjectID = Object.ID
where TypeID = 1
group by ObjID
;
Now, for each TypeID you will need to construct a code generator that can create the source code wallpaper ... max(case when ... construct. from the TypeFields data.
Here is one way:
* Now a codegener macro that can produce the example realization: ;
%macro realize (TypeID=, out=);
%local wallpaper;
proc sql noprint;
select cat
(
', max(case when FieldID=', cats(ID), ' then '
, case
when NameType='str' then 'Value'
when NameType='int' then 'input(Value,12.)'
else 'cats(Value) || " (' || NameType || ') unhandled"'
end
, ' end)'
, ' as ', Name
) length=32000
into :wallpaper separated by ' '
from TypeFields
where TypeID = &TypeID
;
%put NOTE: wallpaper=%SUPERQ(wallpaper);
create &out as
select
FieldsData.ObjectID as ObjID
, max(Object.Name) as ObjName
, max(Object.Description) as ObjDesc
&wallpaper
from FieldsData
join Object
on FieldsData.ObjectID = Object.ID
group by ObjID
;
quit;
%mend;
options mprint;
%realize(TypeID=1, out=table type1_replicate)
You can modify the codegener so that it will insert rows into an out= instead of creating anew.
You should see that your system is workable but needs alot of attention to become generic. Each FieldType might get format, informat, length, date handling, error handling for out-of-range values for type, field sequence, etc...