Insert into new table and a join table at the same time - sql

I have a table called buyers and a table called sellers and I want to introduce a table called contacts that holds contact details for both buyers and sellers.
Focusing on the buyers first, I want a join table buyers_contacts to join buyers to contacts.
I want to initially fill the contacts table with one entry for each buyer. Then for each contact, I want to create a row in buyers_contacts.
How should I do this?
So far I have come up with this query (with incorrect syntax).
with buyer as (
select name, id from buyers
)
, new_contact as (
insert into contacts (name) select name from buyer
returning id as contact_id, buyer.id as buyer_id
)
insert into buyers_contacts (buyer_id, contact_id) values
(new_contact.buyer_id, new_contact.contact_id);
I have thought about doing this update in two stage, first creating a contact for each buyer, and and then inserting into the buyers_contacts table but I cannot rely on the buyers to have unique names.
I feel like this should be a solved problem but, there are no examples that seem to fit this situation.
Postgres is the database I am using.

Assuming there is some sequence behind the scenes that generates the contact Id when to create a new record:
create sequence contact_id;
I think all you have to do to add the buyers is this:
insert into contacts (contact_id, buyer_id)
select
nextval ('contact_id'), b.id
from buyer b
where not exists (
select null
from contacts c
where c.buyer_id = b.id
)
This will create new contact records at any time for only the buyers who have not already been added to the table.
You can also just rely on the default value:
insert into contacts (buyer_id)
select
b.id
from buyer b
where not exists (
select null
from contacts c
where c.buyer_id = b.id
)

Related

SQL insert multiple rows depending on number of rows returned from subquery

I have 3 SQL tables Companies, Materials and Suppliers as follows.
Tables
I need to insert values into Suppliers from a list which contains Company Name and Material Name as headers. However, I have multiple companies with the same name in the database and i need to add a new value into suppliers for each one of those companies.
For e.g. my list containes values ['Wickes','Bricks'] . I have this sql below to add a new entry into the suppliers table but since i have multple companies called 'Wickes' I'll get an error as the subquery will return more than 1 value.
INSERT INTO Suppliers(Id,CompanyId,MaterialId) VALUES (NEWID(), (SELECT Id FROM Companies WHERE Name = 'Wickes'),(SELECT Id FROM Materials WHERE Name = 'Bricks'))
Whats the best solution to get the Id of all the companies there are called 'Wickes' and then add vales into the suppliers table with that Id and the relevant material Id of 'Bricks'.
You can use INSERT () SELECT.. rather than INSERT () VALUES(), e.g
INSERT INTO Suppliers (Id, CompanyId, MaterialId)
SELECT NEWID(), c.Id, m.Id
FROM Companies AS c
CROSS JOIN Materials AS m
WHERE c.Name = 'Wickes'
AND m.Name = 'Bricks';
This will ensure that if you have multiple companies/materials with the same name, all permutations are inserted. Example on db<>fiddle
Although based on your image Suppliers.Id is an integer, so I think NEWID() is not doing what you think it is here, you probably just want:
INSERT INTO Suppliers (CompanyId, MaterialId)
SELECT c.Id, m.Id
FROM Companies AS c
CROSS JOIN Materials AS m
WHERE c.Name = 'Wickes'
AND m.Name = 'Bricks';
And let IDENTITY take care of the Id column in Suppliers.
As a further aside, I've also just noted that MaterialId is VARCHAR in your Suppliers table, that looks like an error if it is supposed to reference the integer Id column in Materials.
INSERT INTO Suppliers(Id,CompanyId,MaterialId) VALUES (NEWID(), (SELECT distict Id FROM Companies WHERE Name = 'Wickes'),(SELECT distict Id FROM Materials WHERE Name = 'Bricks'));
If I understand rightly Companies are the suppliers and the Suppliers table is the one that says where you can buy each material from.
Why do you have duplicates? Do you have an account for different branches of Wickes for example? If they are really duplicates and you don't care which one you use a function like MIN() will do the job of ensuring that only one value is returned. If you have duplicates it would be a good idea to find a way of disactivating all except one. This will make is simpler for you everytime you want to deal with the supplier: minimum orders, chasing overdue orders, payments etc.
Also Companies.ID and Materials.ID should be foreign keys of the Suppliers table. It is also a good idea for the ID column to be auto-incrementing, which makes it easier to add new products as you do not need to specify the ID column.
If you cannot or do not want to modify the id column to auto-incrementing IDENTITY you can continue to use NEWID().
create table Companies(
id INT PRIMARY KEY NOT NULL IDENTITY,
name VARCHAR(25));
create table Materials(
id INT PRIMARY KEY NOT NULL IDENTITY,
name VARCHAR(25));
create table Suppliers(
id INT PRIMARY KEY NOT NULL IDENTITY,
CompanyId INT FOREIGN KEY REFERENCES Companies(id),
MaterialId INT FOREIGN KEY REFERENCES Materials(id)
);
INSERT INTO Companies (name) VALUES ('Wickes');
INSERT INTO Materials (name) VALUES ('Bricks');
INSERT INTO Suppliers ( CompanyId, MaterialId)
SELECT c.Id, M.Id
FROM Companies AS c
CROSS JOIN Materials AS m
WHERE c.Name = 'Wickes'
AND m.Name = 'Bricks';
SELECT * FROM Companies;
SELECT * FROM Materials;
SELECT * FROM Suppliers;
GO
id | name
-: | :-----
1 | Wickes
id | name
-: | :-----
1 | Bricks
id | CompanyId | MaterialId
-: | --------: | ---------:
1 | 1 | 1
db<>fiddle here
INSERT INTO SUPPLIERS
(ID, COMPANYID, MATERIALID)
VALUES (NEWID(),
(SELECT DISTINCT ID FROM COMPANIES WHERE NAME = 'Wickes'), (SELECT DISTINCT ID FROM MATERIALS WHERE NAME = 'Bricks'))

SELECT with JOIN and condition and without JOIN in one query

In PostgreSQL I have two tables:
company
id
name
owner
company_id
verified
There is no owner records, presented in DB without associated company.
But we have company records, presented in DB without owner.
How to select all companies with only verified owners and companies without owners with one query?
I've tried many queries and no one is working :(
For example, this query is not working:
select count(c.id)
from company as c
left outer join owner o on c.id = o.company_id and o.verified is not null
where not (c.id = o.company_id and o.verified is null);
Sample schema
http://sqlfiddle.com/#!17/ab366
create table company (
id int unique,
name varchar(255)
);
create table owner (
first_name varchar(255),
company_id int unique references company(id) on update cascade on delete set null,
verified boolean
);
insert into company values (1, 'company1');
insert into company values (2, 'company2');
insert into company values (3, 'company3');
insert into owner values ('owner1', 1, true);
insert into owner values ('owner2', 2, false);
I need to select company1 and company3.
I would actually use a left join from the company table to the owner table here:
SELECT c.*
FROM company c
LEFT JOIN owner o
ON c.id = o.company_id
WHERE
o.company_id IS NULL OR -- companies without owners
o.verified IS NOT NULL; -- companies with verified owners
I would use filtering in the where clause:
select c.*
from companies c
where not exists (select 1
from owners o
where o.company_id = c.id and
not o.verified
);
The primary reason for this is that a left join version can return duplicates if there are multiple verified owners.
A secondary reason is that this more closely captures the logic that you are describing . . . you want companies that have no unverified owners.

How to stop a database result from re-appearing once value is taken

I have a car rental project, when renting an available car from database table CAR with car ID as primary key, I insert data into table RESERVATION with reservation ID as primary key and car ID as foreign key from car table.
However, I don't want the car I just rented out to appear as an "available car" to rent. What query should I write in order to hide it?
Select cars.*
from cars, reservation
where ?
You should not need to update any tables to hide the car.
When selecting the list of available cars for display, use this SQL:
SELECT * FROM Car WHERE CarID NOT IN (SELECT CarID FROM Reservation)
This will exclude any cars that have already been reserved. That's the basic idea, anyway.
It's likely that reservations are only good for a certain period, and the user may be attempting to reserve a different period, so you might need something more complicated, like:
SELECT * FROM Car WHERE CarID NOT IN
(
SELECT CarID
FROM Reservation
WHERE StartDate < #DesiredEndDate
AND EndDate > #DesiredStartDate
)
This will provide a list of cars that do not appear in a reservation that overlaps the user's desired reservation time.
You can write a join query as:
Select C.carID
from #Car C
Left join #Reservation R on C.carID = R.carID
where R.reservationID is null

SQL query to exclude rows within same table with duplicate field values

I'll try to be brief.
I have a single table that I need to get data from, Contacts.
There is a Company field and they can be in either company A or company B.
All contacts in company B will have a duplicate in Company A, that can be matched by another column LegacyID.
What I'm trying to get is all the Company A rows that DO NOT have someone in Company B with the same LegacyID.
I've tried AND LegacyID NOT IN (SELECT LegacyID FROM Contacts WHERE Company = 'B') as well as AND NOT EXISTS (SELECT LegacyID FROM Contacts WHERE Company = 'B').
Neither of the above work because there are 260,000 Company A contacts and 96,000 Company B. NOT IN and NOT EXISTS worked when there were less than 50,000 rows total, but now with data migration, the query is timing out or running forever.
Thanks
If I understand correctly, NOT EXISTS should do what you want:
select c.*
from contacts c
where companyId = 'A' and
not exists (select 1
from contacts c2
where c2.legacyId = c.legacyId and c2.companyId = 'B'
);
For performance, you want an index on contacts(legacyId, companyId).

How to get data by using different column values refrencing same table

I have two tables:
create table
books (
id int
,bookname text
);
create table
users(
id int
,name text
,book_1 int
,book_2 int
,book_3 int
);
Now, 'book_1', 'book_2', 'book_3' contains id of table 'books'.
I am trying to create a single query using join to get the all three book names with user name.
I am able to get one book name, but how will I get all the three books name?
SELECT user.name
,books.name
FROM user LEFT JOIN books ON books.id=user.book_1;
(This is giving me one book detail)
Using PostgreSQL.
I want result in one row. like
username, book_1_name, book_2_name, book_3_name
Don't want multiple rows.
You can use sub-selects to get bookname of each username in a single row (if id in table books is unique)
select name username
,(select bookname from books where id=book_1) book1_name
,(select bookname from books where id=book_2) book2_name
,(select bookname from books where id=book_3) book3_name
from users
> SQLFIDDLE DEMO
This may help you
SELECT user.name
,books.name
FROM user LEFT JOIN books ON books.id in (select id from books)
SELECT user.name
,books.name
FROM user LEFT JOIN books ON books.id=user.book_1
OR books.id=user.book_2
OR books.id=user.book_3;
join on all id's