Insert two tables value to already exist tables - sql

I have two tables album_new and song_new. song_new table had column album_id, by this relation i can know which album has how many song. where album_id and song_id are auto-increment.
album_new
album_id | album_name
---------+-----------
1 | abs
2 | def
song_new
song_id | album_id | song_name
1 | 1 | yahoo
2 | 1 | lalala
3 | 2 |kukukuku
when i insert the value of one table to another table like this
INSERT INTO album(album_name) SELECT album_name FROM album_name
the album_id changed and i can't insert song_new table table to song table. because the relation is inaccurate now. how can i insert these two table value to two another table album and song which had already some values. i can do this by server side code by using foreach loop but can i do it in sql? can any body provide me the syntax.
Edit: here are the two table where i want to insert the values of above tables
Album
album_id | album_name
---------+-----------
1 | aaa
2 | bbb
Song
song_id | album_id | song_name
1 | 1 | old_song1
2 | 2 | old_song2
3 | 2 | old_song3

If you have already transferred your albums, you have to use the album name as a pivot..
Insert into song_new(album_id, song_name) (select a.album_id, c.song_name from song c join album b on b.album_id=c.album_id join album_new a on a.album_name=b.album_name)
In that situation you will have problems if there are plus that one album with the same name.
It was better to increment the id in album and in song table by the result of select max(album_id) from album_newand then transfer the entire tables in the new two with insert into and select *.
NEW
If you haven't.. Then is simple..
alter table album set id_album=id_album+(select max(id_album from album_new))
alter table song set id=album=id_album+(select max(id_album from album_new))
insert into album_new(select * from album)
insert into song_new(album_id, song_name) (select album_id, song_name from song)

Related

Making combinations of attributes unique in PostgreSQL

There is an option in postgresql where we can have a constraint such that we can have multiple attributes of a table together as unique
UNIQUE (A, B, C)
Is it possible to take attributes from multiple tables and make their entire combination as unique in some way
Edit:
Table 1: List of Book
Attributes: ID, Title, Year, Publisher
Table 2: List of Author
Attributes: Name, ID
Table 3: Written By: Relation between Book and Author
Attributes: Book_ID, Author_ID
Now I have situation where I don't want (Title, Year, Publisher, Authors) get repeated in my entire database
There are 3 solutions to this problem:
You add a column "authorID" to the table "book", as a foreign key. You can then add the UNIQUE constraint to the table "book".
We can have a foreign key on the 2 columns (bookID, author ID) which references the table bookAuthor.
You create a Trigger on insert on the table "book" which checks whether the combination exist and does not insert if it does exist. You will find a working example of this option below.
Whilst working on this option I realised that the JOIN to WrittenBy must be done on Title and not ID. Otherwise we can record the same book as many times as we like just by using a new ID. The problem with using the title is that the slightest change in spelling or punctuation means that it is treated as a new title.
In the example the 3rd insert has failed because it already exists. In the 4th have left 2 spaces in "Tom Sawyer" and it is accepted as a different title.
Also as we use a join to find out the author the real effect of our rule is exactly the same as if we had a UNIQUE constraint on the table books on columns Title, Year and Publisher. This means that all that I have coded is a waste of time.
We thus decide, after coding it, that this option is not effective.
We could create a fourth table with the 4 columns and a UNIQUE constraint on all 4. This seems a heavy solution compared to option 1.
CREATE TABLE Book (
ID int primary key,
Title varchar(25),
Year int,
Publisher varchar(10) );
CREATE TABLE Author (
ID int primary key,
Name varchar(10)
);
CREATE TABLE WrittenBy(
Book_ID int primary key,
Titlew varchar(25),
Author_ID int
);
CREATE FUNCTION book_insert_trigger_function()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS $$
DECLARE
authID INTEGER;
coun INTEGER;
BEGIN
IF pg_trigger_depth() <> 1 THEN
RETURN NEW;
END IF;
SELECT MAX(Author_ID) into authID
FROM WrittenBy w
WHERE w.Titlew = NEW.Title;
SELECT COUNT(*) INTO coun FROM
Book b LEFT JOIN WrittenBy w ON
b.Title = w.Titlew
WHERE NEW.year = b.year
AND NEW.title=b.title
AND NEW.publisher=b.publisher
AND authID = COALESCE(w.Author_ID,authID);
IF coun > 0 THEN
RETURN null; -- this means that we do not insert
ELSE
RETURN NEW;
END IF;
END;
$$
;
CREATE TRIGGER book_insert_trigger
BEFORE INSERT
ON Book
FOR EACH ROW
EXECUTE PROCEDURE book_insert_trigger_function();
INSERT INTO WrittenBy VALUES
(1,'Tom Sawyer',1),
(2,'Huckleberry Finn',1);
INSERT INTO Book VALUES (1,'Tom Sawyer',1950,'Classics');
INSERT INTO Book VALUES (2,'Huckleberry Finn',1950,'Classics');
INSERT INTO Book VALUES (3,'Tom Sawyer',1950,'Classics');
INSERT INTO Book VALUES (3,'Tom Sawyer',1950,'Classics');
SELECT *
FROM Book b
LEFT JOIN WrittenBy w on w.Titlew = b.Title
LEFT JOIN Author a on w.author_ID = a.ID;
>
> id | title | year | publisher | book_id | titlew | author_id | id | name
> -: | :--------------- | ---: | :-------- | ------: | :--------------- | --------: | ---: | :---
> 1 | Tom Sawyer | 1950 | Classics | 1 | Tom Sawyer | 1 | null | null
> 2 | Huckleberry Finn | 1950 | Classics | 2 | Huckleberry Finn | 1 | null | null
> 3 | Tom Sawyer | 1950 | Classics | null | null | null | null | null
>
db<>fiddle here

PostgreSQL: Extract column into separate table and update other column with related key

I have a table product with column product_image_path. These parts of the data need to be moved to a related table, image, which has a separate column image_path and a surrogate key id. The tables are related through the foreign key product.image_id. Is there a way to insert all values of product_image_path into the image table and immediately update the product.image_id to refer to the rows that were just created?
This has to be done in SQL or PL/pgSQL as it is part of a series of database migrations, which do not allow arbitrary scripting.
product:
id | product_image_path
----+-------------------
1 | foo.jpg
2 | bar.jpg
3 | foo.jpg
Should become:
product:
id | image_id
---+---------
1 | 1
2 | 2
3 | 3
image:
id | image_path
---+-----------
1 | foo.jpg
2 | bar.jpg
3 | foo.jpg
If the new image id can be the same as the product id, this is quite easy:
Create the new table:
create table image (id serial primary key, image_path text);
Copy the data from the product table:
insert into image (id, image_path)
select id, product_image_path
from product;
Adjust the sequence for the image.id column:
select setval(pg_get_serial_sequence('image', 'id'), (select max(id) from image));
Add the new image_id column and populate it:
alter table product add image_id integer;
update product set image_id = id;
Get rid of the old_column:
alter table product drop product_image_path;

Update some columns of a record if that record is being inserted during a BulkInsert

Table on DB:
TABLE ALBUM
- ID_ON_DISCOGS (unique)
- ARTIST_ID
- LABEL_ID
I have to bulk insert records into this table.
The problem is that sometimes it could happen that the bulk insert tries to insert a record with a ID_ON_DISCOGS that is already present.
In that case I'd like to update the ARTIST_ID or the LABEL_ID fields with the new values.
Example:
ALBUM RECORD already in DB:
|ID_ON_DISCOGS | ARTIST_ID | LABEL_ID|
:------------------------------------:
|123 | 345 | null |
:------------------------------------:
ALBUM RECORD I try to insert:
|ID_ON_DISCOGS | ARTIST_ID | LABEL_ID|
:------------------------------------:
|123 | null | 567 |
:------------------------------------:
result record I'd like to achieve:
|ID_ON_DISCOGS | ARTIST_ID | LABEL_ID|
:------------------------------------:
|123 | 345 | 567 |
:------------------------------------:
Possibly a solution that could fit in a content provider.
Personally, I would perform the bulk insert first. Once it has completed, I would perform an update statement. Maybe try updating with a CTE? See mobsites answer for example: SQL UPDATE row Number
This will update all records that have a matching ID_ON_DISCOGS.
REPLACE INTO ALBUM
(ID_ON_DISCOGS, ARTIST_ID, LABEL_ID)
SELECT src.ID_ON_DISCOGS, src.ARTIST_ID, src.LABEL_ID
FROM ImportTable src
INNER JOIN ALBUM dest
ON src.ID_ON_DISCOGS = dest.ID_ON_DISCOGS
The next query will then insert the remaining records not in the table already.
INSERT INTO ALBUM
SELECT imp.ID_ON_DISCOGS
,imp.ARTIST_ID
,imp.LABEL_ID
FROM ImportTable imp
WHERE imp.ID_ON_DISCOGS NOT IN (SELECT ID_ON_DISCOGS FROM ALBUM)

Creating a database. Design help needed

I am practicing python flask and I am going to make a very simple music site and am now creating the database.
I am new to databases so I just wanted help to see if the tables and relations are correct.
Also would I store multiple song ID's in the playlist_songs songID column?
userTable
userID (PK), username, email, password, level
songTable
songID (PK), songName, songArtist, songGenre, songDuration
playlistTable
playlistID (PK), userID (FK), playlistName, playlistDescription
playlist_songs
playlistID (FK), songID (FK)
As requested, I'm adding some collective info based on your question and comments.
Your design looks fine. As recommended by Rowland, it could perhaps use an order column. Something to order by. If you choose not to add this the songs will be returned in a somewhat random order for the playlist, or you could order by the SongId column and be guaranteed the same order at least (within a playlist). But it wouldn't be changeable.
You asked how data was entered in to the playlist_songs table:
SongTable
SongId | SongName | ...
-----------------------------
1 | Happy Bithday | ...
2 | Last Christmas | ...
3 | Christmas tree | ...
4 | Some song | ...
PlaylistTable
PlaylistId | PlaylistName | ...
-------------------------------------
1 | My Birthday songs | ...
2 | My Christmas songs | ...
3 | All my songs | ...
Playlist_songs
PlaylistId (FK) | SongId (FK)
-----------------------------
1 | 1
2 | 2
2 | 3
3 | 1
3 | 2
3 | 3
3 | 4
As you can see the Playlist_songs table can contain many playlists and many songs. If you query Playlist_songs for PlaylistId = 2 it will return SongId 2 & 3, and so on.
Currently, a primary key would have to be a constraint on the two columns (a compound key). This is also where you could add an Order column, or just add a stand alone primary key (Id for example) and order by that.

How do I delete duplicate records, or merge them with foreign-key restraints intact?

I have a database generated from an XML document with duplicate records. I know how to delete one record from the main table, but not those with foreign-key restraints.
I have a large amount of XML documents, and they are inserted without caring about duplicates or not. One solution to removing duplicates is to just delete the lowest Primary_Key values (and all related foreign key records) and keep the highest. I don't know how to do that, though.
The database looks like this:
Table 1: [type]
+-------------+---------+-----------+
| Primary_Key | Food_ID | Food_Type |
+-------------+---------+-----------+
| 70001 | 12345 | fruit |
| 70002 | 12345 | fruit |
| 70003 | 12345 | meat |
+----^--------+---------+-----------+
|
|-----------------|
|
| Linked to primary key in the first table
+-------------+--------v--------+-------------+-------------+------------+
| Primary_Key | Information_ID | Food_Name | Information | Comments |
+-------------+-----------------+-------------+-------------+------------+
| 0001 | 70001 | banana | buy # toms | delicious! |
| 0002 | 70002 | banana | buy # mats | so-so |
| 0003 | 70003 | decade meat | buy # sals | disgusting |
+-------------+-----------------+-------------+-------------+------------+
^ Table 2: [food_information]
There are several other linked tables as well, which all have a foreign key value of the matched primary key value in the main table ([type]).
My question based on which solution might be best:
How do I delete all of those records, except 70003 (the highest one)? We can't know if it's duplicate record unless [Food_ID] shows up more than once. If it shows up more than once, we need to delete records from ALL tables (there are 10) based on the Primary_Key and Foreign_Key relationship.
How do I update/merge these SQL records on insertion to avoid having to delete multiples again?
I'd prefer #1, as it prevents me from having to rebuild the database, and it makes inserting much easier.
Thanks!
Even if a [foodID] is not duplicated you will get a max(Primary_Key)
And it will not be deleted
The where condition is NOT in
delete tableX
where tableX.informationID not in ( select max(Primary_Key)
from [type]
group by [foodID] )
then just do [type] last
delete [type]
where [type].[Primary_Key] not in ( select max(Primary_Key)
from [type]
group by [foodID] )
then just create as unique constraint on [foodID]
something like...
assumed:
create table food (
primary_key int,
food_id int,
food_type varchar(20)
);
insert into food values (70001,12345,'fruit');
insert into food values (70002,12345,'fruit');
insert into food values (70003,12345,'meat');
insert into food values (70004,11111,'taco');
create table info (
primary_key int,
info_id int,
food_name varchar(20)
);
insert into info values (1,70001,'banana');
insert into info values (2,70002,'banana');
insert into info values (3,70003,'decade meat');
insert into info values (4,70004,'taco taco');
and then...
-- yields: 12345 70003
select food_id, max(info_id) as max_info_id
from food
join info on food.primary_key=info.info_id
where food_id in (
select food_id
from food
join info on food.primary_key=info.info_id
group by food_id
having count(*)>1);
then... something like... this to get the ones to delete. there might be a better way to write this... i'm thinking about it.
select *
from food
join info on food.primary_key=info.info_id
join ( select food_id, max(info_id) as max_info_id
from food
join info on food.primary_key=info.info_id
where food_id in (
select food_id
from food
join info on food.primary_key=info.info_id
group by food_id
having count(*)>1)
) as dont_delete
on food.food_id=dont_delete.food_id and
info.info_id<max_info_id
gives you:
PRIMARY_KEY FOOD_ID FOOD_TYPE INFO_ID FOOD_NAME MAX_INFO_ID
70001 12345 fruit 70001 banana 70003
70002 12345 fruit 70002 banana 70003
so you could do.... just delete from food where primary_key in (select food.primary_key from that_big_query_up_there) and delete from info where info_id in (select food.primary_key from that_big_query_up_there)
for future issues, maybe consider a unique constraint on food... unique(primary_key,food_id) or something but if it's one-to-one, why don't you just store them together...?