How to update a column based on matching ID's - sql

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

Related

Join operations in SQL

I should retrieve the IDs and names of ingredients that are not contained by any ice cream and then
sort the output rows in ascending order by ingredient ID. I don't quite understand how JOIN -operation works in this exercise.
I tried e.g. the following but it gives too many rows in the output.
SELECT ingredient.ingredient_id, ingredient.ingredient_name
FROM ingredient
LEFT JOIN contains ON ingredient.ingredient_id != contains.ingredient_id
LEFT JOIN ice_cream ON ice_cream.ice_cream_id != contains.ice_cream_id
ORDER BY ingredient.ingredient_id;
Output columns should be as follows:
ingredient_id ingredient_name
----------------------------
6 Dark chocolate
and here are the tables:
CREATE TABLE manufacturer (
manufacturer_id INT,
manufacturer_name VARCHAR(30) NOT NULL,
country VARCHAR(30) NOT NULL,
PRIMARY KEY (manufacturer_id),
UNIQUE (manufacturer_name)
);
-- jäätelöitä
-- ice creams
CREATE TABLE ice_cream (
ice_cream_id INT,
ice_cream_name VARCHAR(30) NOT NULL,
manufacturer_id INT NOT NULL,
manufacturing_cost NUMERIC(4,2),
PRIMARY KEY (ice_cream_id),
UNIQUE (ice_cream_name),
FOREIGN KEY (manufacturer_id) REFERENCES manufacturer
);
-- aineksia
-- ingredients
-- plant_based arvo 0 merkitse ei ja arvo 1 merkitsee kyllä
-- plant_based value 0 means no and value 1 means yes
CREATE TABLE ingredient (
ingredient_id INT,
ingredient_name VARCHAR(30) NOT NULL,
kcal INT,
protein NUMERIC(3,1),
plant_based INT,
PRIMARY KEY (ingredient_id),
UNIQUE (ingredient_name)
);
-- jäätelöt sisältävät aineksia
-- ice creams contain ingredients
CREATE TABLE contains(
ice_cream_id INT NOT NULL,
ingredient_id INT NOT NULL,
quantity INT,
PRIMARY KEY (ice_cream_id, ingredient_id),
FOREIGN KEY (ice_cream_id) REFERENCES ice_cream,
FOREIGN KEY (ingredient_id) REFERENCES ingredient
);
--Ice cream manufacturers
INSERT INTO manufacturer VALUES (
1, 'Jen & Berry', 'Canada'
);
INSERT INTO manufacturer VALUES (
2, '4 Friends', 'Finland'
);
INSERT INTO manufacturer VALUES (
3, 'Gelatron', 'Italy'
);
--Ice cream
INSERT INTO ice_cream VALUES (
1, 'Plain Vanilla', 1, 1.00
);
INSERT INTO ice_cream VALUES (
2, 'Vegan Vanilla', 2, 0.89
);
INSERT INTO ice_cream VALUES (
3, 'Super Strawberry', 2, 1.44
);
INSERT INTO ice_cream VALUES (
4, 'Very plain', 2, 1.20
);
--Ingredients
INSERT INTO ingredient VALUES (
1, 'Cream', 400, 3, 0
);
INSERT INTO ingredient VALUES (
2, 'Coconut cream', 230, 2.3, 1
);
INSERT INTO ingredient VALUES (
3, 'Sugar', 387, 0, 1
);
INSERT INTO ingredient VALUES (
4, 'Vanilla extract', 12, 0, 1
);
INSERT INTO ingredient VALUES (
5, 'Strawberry', 33, 0.7, 1
);
INSERT INTO ingredient VALUES (
6, 'Dark chocolate', 535, 8, 1
);
--Contains
INSERT INTO contains VALUES (
1, 1, 70
);
INSERT INTO contains VALUES (
1, 3, 27
);
INSERT INTO contains VALUES (
1, 4, 3
);
INSERT INTO contains VALUES (
2, 2, 74
);
INSERT INTO contains VALUES (
2, 3, 21
);
INSERT INTO contains VALUES (
2, 4, 5
);
INSERT INTO contains VALUES (
3, 1, 60
);
INSERT INTO contains VALUES (
3, 3, 10
);
INSERT INTO contains VALUES (
3, 5, 30
);
INSERT INTO contains VALUES (
4, 2, 95
);
INSERT INTO contains VALUES (
4, 4, 5
);
You can join the "contains" table with the "ingredient" table using a LEFT JOIN and filter all values that have NULL in the "contains.ice_cream_id" field:
SELECT i.ingredient_id,
i.ingredient_name
FROM ingredient i
LEFT JOIN contains c
ON i.ingredient_id = c.ingredient_id
WHERE c.ice_cream_id IS NULL
It appears you are asking to find where somethig does not exist, so it would make sense to express that using not exists
So you just need to find the ingredients that don't exist in the list of contents:
select ingredient_id, ingredient_name
from ingredient i
where not exists (
select * from contains c
where c.ingredient_id = i.ingredient_id
);
Demo Fiddle

SQL Data Manipulation (Adding FK and Displaying one Data from different table)

I'm new in SQL and we have an activity. I only have two problems in my query, first is adding the FK because when I run it, this will have an error that show like this: near "FOREIGN": syntax error and I don't know why.
Second is I dont know how to display the Products, Vendor ID and Name that comes from different table (This is the part of the instruction:
Determine which products have a quantity of less than 1000 then display the products Vendor ID and Name)
CREATE TABLE Products (ProductID int NOT NULL PRIMARY KEY, Description varchar (100), Quantity
int, Price int (50), VendorID int NOT NULL);
CREATE TABLE Vendors (VendorID int NOT NULL PRIMARY KEY, Name varchar (50), ContactNum int);
ALTER TABLE Products ADD FOREIGN KEY (VendorID) REFERENCES Vendors (VendorID);
INSERT INTO Vendors VALUES ('V00001', 'Universal Corporation','8633-76131');
INSERT INTO Vendors VALUES('V00002', 'Liwayway Corporation','8844-8441');
INSERT INTO Vendors VALUES ('V00003', 'Monde Nissin', '7759-5000');
Select * FROM Vendors;
INSERT INTO Products VALUES ('P000101', 'Jack N Jill Piattos', 1000, 15, 'V00001');
INSERT INTO Products VALUES ('P000102', 'Jack N Jill Nova', 1000, 15, 'V00001');
INSERT INTO Products VALUES ('P000105', 'Oishi Prawn Crackers', 700, 8, 'V00002');
INSERT INTO Products VALUES ('P000107', 'Nissin Eggnog Cookies', 850, 7, 'V00003');
SELECT * FROM Products;
UPDATE Products SET Quantity = Quantity + 274 WHERE ProductID = 'P000101';
UPDATE Products SET Quantity = Quantity - 42 WHERE ProductID = 'P000107';
SELECT * FROM Products;
SELECT DISTINCT ProductID, Description, Quantity FROM Products WHERE Quantity < 1000;

SQL: Update and Insert with condition

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

UPDATE source table, AFTER Grouping?

I have a table (source) with payments for a person - called 'Item' in the example below.
This table will have payments for each person, added to it over a period.
I then generate invoices, which basically takes all the payments for a particular person, and sums them up into a single row.
This must be stored in an invoice table, for auditing reasons.
I do this in the example below.
What I am missing, though, as I am not sure how to do it, is that each payment, once assigned to the Invoice table, needs to had the Invoice ID that it was assigned to, stored in the Items table.
So, see the example below:
CREATE TABLE Items
(
ID INT NOT NULL IDENTITY(1,1),
PersonID INT NOT NULL,
PaymentValue DECIMAL(16,2) NOT NULL,
AssignedToInvoiceID INT NULL
)
CREATE TABLE Invoice
(
ID INT NOT NULL IDENTITY(1,1),
PersonID INT NOT NULL,
Value DECIMAL(16,2)
)
INSERT INTO Items (PersonID, PaymentValue) VALUES (1, 100)
INSERT INTO Items (PersonID, PaymentValue) VALUES (2, 132)
INSERT INTO Items (PersonID, PaymentValue) VALUES (2, 65)
INSERT INTO Items (PersonID, PaymentValue) VALUES (1, 25)
INSERT INTO Items (PersonID, PaymentValue) VALUES (3, 69)
SELECT * FROM Items
INSERT INTO Invoice (PersonID, Value)
SELECT PersonID, SUM(PaymentValue) FROM Items
WHERE AssignedToInvoiceID IS NULL
GROUP BY PersonID
SELECT * FROM Invoice
DROP TABLE Items
DROP TABLE Invoice
What I need to do is then update the Items table, to say that the first row has been assigned to Invoice.ID 1, row two was assigned to Invoice ID 2. Row 3, was assigned to Invoice ID 2 as well.
Note, there are many other columns in the table. This is a basic example.
Simply, I need to record which invoice, each source row was assigned to.
The key thing here to ensure payments are correctly linked to invoices is to ensure that:
A: No updates are made to Items between reading the unassigned items and updating AssignedToInvoiceID.
B: No new invoices are created with the Items being process before updating AssignedToInvoiceID.
As you are updating two tables it will have to be a two step process. To ensure A it will need to be run in a transaction with a least REPEATABLE READ isolation. To ensure B requires a transaction with SERIALIZABLE isolation. See SET TRANSACTION ISOLATION LEVEL
It can be done like this:
BEGIN TRAN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
DECLARE #newInvoices TABLE (PersonID INT, InvoiceID INT)
INSERT INTO Invoice (PersonID, Value)
OUTPUT inserted.ID, inserted.PersonID INTO #newInvoices(InvoiceID, PersonID)
SELECT PersonID, SUM(PaymentValue) FROM Items
WHERE AssignedToInvoiceID IS NULL
GROUP BY PersonID
UPDATE Items
SET AssignedToInvoiceID = InvoiceID
FROM Items
INNER JOIN #newInvoices newInvoice ON newInvoice.PersonID = Items.PersonID
WHERE AssignedToInvoiceID IS NULL
COMMIT
An alternative if you are using SQL Server 2012 or later is to use the SEQUNCE object, this will allow the Items to be assigned new invoice IDs before the Invoices are created, reducing the locking required.
It works like this:
-- Run once with your table setup.
CREATE SEQUENCE InvoiceIDs AS INT START WITH 1 INCREMENT BY 1
CREATE TABLE Items
(
ID INT NOT NULL IDENTITY(1,1),
PersonID INT NOT NULL,
PaymentValue DECIMAL(16,2) NOT NULL,
AssignedToInvoiceID INT NULL
)
CREATE TABLE Invoice
(
-- No longer a IDENTITY column
ID INT NOT NULL,
PersonID INT NOT NULL,
Value DECIMAL(16,2)
)
BEGIN TRAN
DECLARE #newInvoiceLines TABLE (PersonID INT, InvoiceID INT, PaymentValue DECIMAL(16,2))
-- Reading and updating AssignedToInvoiceID happens in one query so is thread safe.
UPDATE Items
SET AssignedToInvoiceID = newInvoices.InvoiceID
OUTPUT inserted.PersonID, inserted.AssignedToInvoiceID, inserted.PaymentValue
INTO #newInvoiceLines(PersonID, InvoiceID, PaymentValue)
FROM Items
INNER JOIN (
SELECT PersonID, NEXT VALUE FOR InvoiceIDs AS InvoiceID
FROM Items
GROUP BY PersonID
) AS newInvoices ON newInvoices.PersonID = Items.PersonID
WHERE Items.AssignedToInvoiceID IS NULL
INSERT INTO Invoice (ID, PersonID, Value)
SELECT InvoiceID, PersonID, SUM(PaymentValue) FROM #newInvoiceLines
GROUP BY PersonID, InvoiceID
COMMIT
You will still want to use a transaction to ensure the Invoice gets created.
Based on what I understand, you could run an update from join after you have inserted records into Invoices table, like so:
update items
set assignedtoinvoiceid = v.id
from
items m
inner join invoice v on m.personid = v.personid
Demo
Each time you do an invoice "run", select the most recent invoice for each person something like,
update items
set AssignedToInvoiceID = inv.id
from
(select personid, max(id) id
from invoice
group by personid) inv
where items.personid = inv.personid
and AssignedToInvoiceID is null
this assumes that AssignedToInvoiceID is null when it isn't populated, if it gets defaulted to an empty string or something then you would need to change the where condition.
1) Get MAX(ID) from Invoice table before inserting new rows from Items table. Store this value into a variable: #MaxInvoiceID
2) After inserting records into Invoice table, update AssignedToInvoiceID in Items table with Invoice.ID>#MaxInvoiceID
Refer below code:
CREATE TABLE #Items
(
ID INT NOT NULL IDENTITY(1,1),
PersonID INT NOT NULL,
PaymentValue DECIMAL(16,2) NOT NULL,
AssignedToInvoiceID INT NULL
)
CREATE TABLE #Invoice
(
ID INT NOT NULL IDENTITY(1,1),
PersonID INT NOT NULL,
Value DECIMAL(16,2)
)
DECLARE #MaxInvoiceID INT;
SELECT #MaxInvoiceID=ISNULL(MAX(ID),0) FROM #Invoice
SELECT #MaxInvoiceID
INSERT INTO #Items (PersonID, PaymentValue) VALUES (1, 100)
INSERT INTO #Items (PersonID, PaymentValue) VALUES (2, 132)
INSERT INTO #Items (PersonID, PaymentValue) VALUES (2, 65)
INSERT INTO #Items (PersonID, PaymentValue) VALUES (1, 25)
INSERT INTO #Items (PersonID, PaymentValue) VALUES (3, 69)
SELECT * FROM #Items
INSERT INTO #Invoice (PersonID, Value)
SELECT PersonID, SUM(PaymentValue)
FROM #Items
WHERE AssignedToInvoiceID IS NULL
GROUP BY PersonID
SELECT * FROM #Invoice
UPDATE Itm
SET Itm.AssignedToInvoiceID=Inv.ID
FROM #Items Itm
JOIN #Invoice Inv ON Itm.PersonID=Inv.PersonID AND Itm.AssignedToInvoiceID IS NULL AND Inv.ID>#MaxInvoiceID
SELECT * FROM #Items
DROP TABLE #Items
DROP TABLE #Invoice

SQL Stored procedure to obtain top customers

I'm trying to create a stored procedure that goes through a "SALES" table and returns the best two customers of a pharmacy (the two customers who have spent more money).
Here's some code:
Table creation:
create table Customer (
Id_customer int identity(1,1) Primary Key,
Name varchar(30),
Address varchar(30),
DOB datetime,
ID_number int not null check (ID_number > 0),
Contributor int not null check (Contributor > 0),
Customer_number int not null check (Customer_number > 0)
)
create table Sale (
Id_sale int identity(1,1) Primary Key,
Id_customer int not null references Customer(Id_customer),
Sale_date datetime,
total_without_tax money,
total_with_tax money
)
Well, I don't know if this is useful but I have a function that returns the total amount spent by a customer as long as I provide the customer's ID.
Here it is:
CREATE FUNCTION [dbo].[fGetTotalSpent]
(
#Id_customer int
)
RETURNS money
AS
BEGIN
declare #total money
set #total = (select sum(total_with_tax) as 'Total Spent' from Sale where Id_customer=#Id_customer)
return #total
END
Can someone help me get the two top customers?
Thanks
Chiapa
PS: Here's some data to insert so you can test it better:
insert into customer values ('Jack', 'Big street', '1975.02.01', 123456789, 123456789, 2234567891)
insert into customer values ('Jim', 'Little street', '1985.02.01', 223456789, 223456789, 2234567891)
insert into customer values ('John', 'Large street', '1977.02.01', 323456789, 323456789, 3234567891)
insert into customer values ('Jenny', 'Huge street', '1979.02.01', 423456789, 423456789, 4234567891)
insert into sale values (1, '2013.04.30', null, 20)
insert into sale values (2, '2013.05.22', null, 10)
insert into sale values (3, '2013.03.29', null, 30)
insert into sale values (1, '2013.05.19', null, 34)
insert into sale values (1, '2013.06.04', null, 21)
insert into sale values (2, '2013.06.01', null, 10)
insert into sale values (2, '2013.05.08', null, 26)
You can do this with a single query without any special functions:
select top 2 c.id_customer, c.name, sum(s.total_with_tax)
from customer c
join sale s on c.id_customer = s.id_customer
group by c.id_customer, c.name
order by sum(s.total_with_tax) desc
This joins onto a CTE with the top customers.
Remove the WITH TIES option if you want exactly 2 and don't want to include customers tied with the same spend.
WITH Top2
AS (SELECT TOP 2 WITH TIES Id_customer,
SUM(total_with_tax) AS total_with_tax
FROM Sale
GROUP BY Id_customer
ORDER BY SUM(total_with_tax) DESC)
SELECT *
FROM Customer C
JOIN Top2 T
ON C.Id_customer = T.Id_customer
I'm not really into SQL Server dialect, but this one will give you best customers in descending order along with money they spent:
select Id_customer, total_with_tax from
(select Id_customer, sum(total_with_tax) total_with_tax from Sale group by Id_customer)
order by total_with_tax desc