postgres insert multiple tables at once with generated columns - sql

Suppose I have this piece of data (in reality roughly 5,000 entries every few seconds, initially 50,000 from a data dump):
[
"person": {
"name": "Johnny Bravo",
"location": {
"zipcode": 11111,
"address": "1 Second Ave"
},
"phone_numbers": [ 15007774321, 12227435432 ]
}
]
and this schema:
CREATE TABLE person(
id serial PRIMARY KEY,
name varchar(255) UNIQUE,
location_id integer NOT NULL REFERENCES location(id)
)
CREATE TABLE location(
id serial PRIMARY KEY,
zipcode integer NOT NULL,
address varchar(255) NOT NULL
)
CREATE TABLE phone_number(
id serial PRIMARY KEY,
"number" integer NOT NULL
)
CREATE TABLE person_number_relationship(
id serial PRIMARY KEY,
phone_number_id integer NOT NULL REFERENCES phone_number(id),
person_id integer NOT NULL REFERENCES person(id)
)
This is an oversimplification but the parts that matter are here. Currently to insert this data I use the language accessing the database (scala) to do most of the work.
Insert all locations and then select all locations (2 queries)
Replace location with location id in persons list
Insert phone numbers and then select all phone numbers (2 queries)
Create a map of phone number's number to id
Insert persons list and then select persons id and name (2 queries)
Create a map of person's name to id
Create a new list of person_id, phone_number_id from the phone_numbers list and the persons map
Insert the new list (1 query)
The question I am asking can be broken into two parts:
How can I insert into two tables where one table requires a generated id from the other?
How can I insert a many to many relationship for two tables when the ids are generated on insert?
Ideally I would like to push all of this logic down to the database where I wont be wasting memory sending extra information between the driver and the database

Related

SQL - cannot add or update a child row: a foreign key constraint fails

I keep getting the error:
Cannot add or update a child row: a foreign key constraint fails
(myWork.Bookingss, CONSTRAINT Bookingss_ibfk_1 FOREIGN KEY
(CustomersID) REFERENCES Customers (CustomersID))
I have spent a while researching as this is apart of a school assignment and can not seem to resolve the problem.
Here is my SQL code:
USE myWork ;
DROP TABLE IF EXISTS Bookingss ;
DROP TABLE IF EXISTS Customers ;
CREATE TABLE myWork.Customers
(
CustomersID INT NOT NULL AUTO_INCREMENT,
Surname CHAR(30) NOT NULL ,
FirstName CHAR(30) NOT NULL ,
Title CHAR(10),
DOB DATE,
HouseNumber INT,
StreetName CHAR(30),
Town CHAR(30),
PostCode CHAR(9),
Telephone INT,
PRIMARY KEY (CustomersID)
) ;
CREATE TABLE myWork.Bookingss
(
BookingsID INT NOT NULL AUTO_INCREMENT,
CustomersID INT NOT NULL,
AdultsBooked INT NOT NULL,
ChildrenBooked INT NOT NULL,
Check_In DATE,
Check_Out DATE,
PRIMARY KEY (BookingsID),
FOREIGN KEY (CustomersID)
REFERENCES myWork.Customers (CustomersID)
) ;
SHOW TABLES ;
INSERT INTO myWork.Bookingss ( CustomersID, AdultsBooked , ChildrenBooked , Check_In , Check_Out )
VALUES ("1", "2", "3", "2022-04-10", "2022-04-13" ) ;
INSERT INTO myWork.Customers ( Surname , FirstName , Title , DOB )
VALUES ( "smith" , "ryan" , "Mr" , "1998-02-16" ) ;
SELECT * FROM Customers , Bookingss;
You got the order wrong and some challenges to overcome. You are trying to insert a booking first, and this with an id for a customer that does not yet exist in the customer table at that moment.
You have to insert the customer first, then use the customer's id to insert the booking for the customer (using his id).
And there is the first challenge. The customer id is an auto increment field. You would not know the id when you insert a customer in the table.
You would have to fetch the customer and use the id of that customer to insert a booking for the customer. How do you fetch the customer? Selecting the customer with a specific name surname and first name is not a correct choice, any other field (or composition of fields) that I see in the table definition is not a good choice neither.
You'd need to think further about a good solution. There are several options I could tell you about. But the appropriate solution depends on your assignment/context.
By the way, the last select clause may not deliver the result you expect. You are producing a cartesian product (every row of the first table with every row of the second table). What you probably want is a JOIN where you link the first table with the second table accordingly (e.g. using the primary key and the foreign key).

How to efficiently insert ENUM value into table?

Consider the following schema:
CREATE TABLE IF NOT EXISTS snippet_types (
id INTEGER NOT NULL PRIMARY KEY,
name TEXT NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS snippets (
id INTEGER NOT NULL PRIMARY KEY,
title TEXT,
content TEXT,
type INTEGER NOT NULL,
FOREIGN KEY(type) REFERENCES snippet_types(id)
);
This schema assumes a one-to-many relationship between tables and allows efficiently maintaining a set of ENUMs in the snippet_types table. Efficiency comes from the fact that we don't need to store the whole string describing snippet type in the snippets table, but this decision also leads us to some inconvenience: upon inserting we need to retrieve snippet id from snippet_types and this leads to one more select and check before inserting:
SELECT id FROM snippet_types WHERE name = "foo";
-- ...check that > 0 rows returned...
INSERT INTO snippets (title, content, type) values ("bar", "buz", id);
We could also combine this insert and select into one select like that:
INSERT INTO snippets (title, content, type)
SELECT ("bar", "buz", id) FROM snippet_types WHERE name = "foo"
However, if "foo" type is missing in snippet_types then 0 rows would have been inserted and no error returned and I don't see a possibility to get a number of rows sqlite actually inserted.
How can I insert ENUM-containing tuple in one query?

Using joins in complex queries

I have three tables customer_cars, bookings, and replaced_parts ad below:
CREATE TABLE customer_cars(
pk_car_id NUMBER(11) NOT NULL,
car_plate_number VARCHAR2(15) NOT NULL
);
CREATE TABLE bookings(
pk_booking_id NUMBER(11),
fk_car_id NUMBER(11),
);
CREATE TABLE replaced_parts(
pk_parts_id VARCHAR2(25),
fk_booking_id NUMBER(11),
replaced_parts parts_varray_type
);
CREATE OR REPLACE TYPE parts_type AS OBJECT (
name VARCHAR2(25),
price NUMBER(10),
);
/
CREATE TYPE parts_varray_type AS
VARRAY(40) OF parts_type;
/
I am trying to get parts name from parts_varray_type of a car. All i have is car_plate_number. Using this plate number i want to find pk_car_id in customer_cars and then that pk_car_if id should match with fk_car_id in bookings to find bookings_id. Then the found booking id should match with fk_booking_id in replaced_parts to get the parts_name.
INSERT INTO customer_cars (pk_car_id, car_plate_number)
VALUES(200000,'5651L');
INSERT INTO bookings(pk_booking_id, fk_car_id)
VALUES(700000,200000);
INSERT INTO replaced_parts (pk_parts_id, fk_booking_id, replaced_parts)
VALUES(700000,600000,
parts_varray_type(
parts_type('CLUTCH', 2000)));
Above is the insert statements. Now using plate number '5651L' i want to display clutch , 2000 from parts parray type.
Something like:
SELECT..
FROM..(Usings joins or any other methods)
WHERE pk_car_id = '5651L';
It should display name and price from parts varray type.

First DB - How to structure required information

I watched a few youtube videos about how to structure a database using tables and fields. I am a bit confused about how to strucuture my information.
I have put my attempt below:
// Identifier Table
// This is where we give each item a new unique identifier
UniqueID []
// Item Table
// This is where the main content goes which is displayed
UniqueID []
Title []
Description []
Date []
Location []
Coordinates []
Source []
Link []
// Misc Table
// This is additional useful information, but not displayed
geocoded []
country name []
By separating out the uniqueID when I delete a record I can make sure that new records still have a unique incrementing ID. Can I get some feedback on how I divided up my data into three tables.
you gave us no hint what you want to represent in your db.
For example: if location and coordinate describe a building or maybe room, than it could be useful to save that information in an extra table and have a relationship from item to it, as this would allow to easily fetch all items connected with on place.
Of course you should apply the same principle for country: a locations lays with-in a country.
BEGIN;
CREATE TABLE "country" (
"id" integer NOT NULL PRIMARY KEY,
"name" varchar(255) NOT NULL
)
;
CREATE TABLE "location" (
"id" integer NOT NULL PRIMARY KEY,
"name" varchar(255) NOT NULL,
"coordinate" varchar(255) NOT NULL,
"country_id" integer NOT NULL REFERENCES "country" ("id")
)
;
CREATE TABLE "item" (
"id" integer NOT NULL PRIMARY KEY,
"title" varchar(25) NOT NULL,
"description" text NOT NULL,
"date" datetime NOT NULL,
"source" varchar(255) NOT NULL,
"link" varchar(255) NOT NULL,
"location_id" integer NOT NULL REFERENCES "location" ("id")
)
;
In the case stated above I would pack everything into one table since there is not enugh complexity to benfit from spliting the data into diferent tables.
When you have more metadata you can split it up into:
Item (For display data)
ItemMeta (For meta data)

Why does this query only select a single row?

SELECT * FROM tbl_houses
WHERE
(SELECT HousesList
FROM tbl_lists
WHERE tbl_lists.ID = '123') LIKE CONCAT('% ', tbl_houses.ID, '#')
It only selects the row from tbl_houses of the last occuring tbl_houses.ID inside tbl_lists.HousesList
I need it to select all the rows where any ID from tbl_houses exists within tbl_lists.HousesList
It's hard to tell without knowing exactly what your data looks like, but if it only matches the last ID, it's probably because you don't have any % at the end of the string, so as to allow for the list to continue after the match.
Is that a database in zeroth normal form I smell?
If you have attributes containing lists of values, like that HousesList attribute, you should instead be storing those as distinct values in a separate relation.
CREATE TABLE house (
id VARCHAR NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE list (
id VARCHAR NOT NULL,
PRIMARY KEY (id),
);
CREATE TABLE listitem (
list_id VARCHAR NOT NULL,
FOREIGN KEY list_id REFERENCES list (id),
house_id VARCHAR NOT NULL,
FOREIGN KEY house_id REFERENCES house (id),
PRIMARY KEY (list_id, house_id)
);
Then your distinct house listing values each have their own tuple, and can be selected like any other.
SELECT house.*
FROM house
JOIN listitem
ON listitem.house_id = house.id
WHERE
listitem.list_id = '123'