Copy selections from two related tables - sql

I have two tables in a database. In the first table (tab1) I have a list of items. In the second table I have a many to many relationship between these items.
CREATE TABLE tab1(id INTEGER PRIMARY KEY ASC,set INTEGER, name TEXT);
CREATE TABLE tab2(id INTEGER PRIMARY KEY ASC,id1 INTEGER,id2 INTEGER,relationship TEXT);
The items in the first table are comprised of sets that all have the same value for the set field. I want to duplicate any given set with a new set id, such that the new set contains the same elements and relationships of the original set. If all the items in the set have sequential ids, I can do it as follows. First, find the highest id in the set (in this case, set 3):
SELECT id FROM tab1 WHERE set=3 ORDER BY id DESC LIMIT 1
I assign this to a variable $oldid. Next, I duplicate the items in tab1 matching the specified set, giving them a new set (in this case 37)
INSERT INTO tab1 (set,name) SELECT 37, name FROM tab1 WHERE set=3 ORDER BY id ASC
I then get the id of the last row inserted, and assign it to the variable $newid:
SELECT last_insert_rowid()
I then assign $diff = $newid-$oldid. Since the original set has sequential ids, I can simply select the original relationships for set=3, then add the difference:
INSERT INTO tab2 (id2,id2,relationship) SELECT id1+$diff,id2+$diff,type FROM tab WHERE id1 IN (SELECT id FROM tab WHERE set=3)
But this does not work if the set is not consisting of sequential ids in tab1. I could do a complete query of the original ids, then create a 1:1 mapping to the newly inserted ids for set 37, and then add the difference between each row, and then insert the newly computed rows in the table. But this requires loading all the selections to the client and doing all the work on the client. Is there some way to create a query that does it on the server in the general case?

Assuming that (set, name) is a candidate key for tab1, you can use these columns to look up the corresponding values:
INSERT INTO tab2(id1, id2, relationship)
SELECT (SELECT id
FROM tab1
WHERE "set" = 37
AND name = (SELECT name
FROM tab1
WHERE id = tab2.id1)),
(SELECT id
FROM tab1
WHERE "set" = 37
AND name = (SELECT name
FROM tab1
WHERE id = tab2.id2)),
relationship
FROM tab2
WHERE id1 IN (SELECT id
FROM tab1
WHERE "set" = 3)
OR id2 IN (SELECT id
FROM tab1
WHERE "set" = 3)

Related

Distinct value in array

Hello i have problem i have table
CREATE TABLE doctor(
idDoc SERIAL PRIMARY KEY,
meds TEXT[]
);
I want to store only unique values if my user enter same meds inside
Table before user insert
id
meds
1
{Nalfon,Ocufen,Actron}
After user insert (insert is Actron,Soma,Robaxin) i want update and have in table old values and new values ( Actron is same)
if I UPDATE table with new values I will get
UPDATE doctor SET meds='{Actron,Soma,Robaxin}') WHERE id=1;
id
meds
1
{Actron,Soma,Robaxin}
But i want
id
meds
1
{Nalfon,Ocufen,Actron,Soma,Robaxin}
I don't know how to check if new value is same like value in table and only insert/update table to have unique values.
Given your data structure, you can unnest the existing meds, add in the new ones, and re-aggregate as a unique array:
UPDATE doctor d
SET meds = (select array_agg(distinct med)
from (select med
from unnest('{Actron,Soma,Robaxin}'::text[]) u(med)
union all
select med
from unnest(d.meds) u(med)
) m
)
WHERE idDoc = 1;
Here is a db<>fiddle.
That said, you might consider a junction table with one row per doctor and medicine. That is the more traditional SQL representation for a many-to-many relationship.

How to update table with field Postgres

I have table like this:
Incident_id is foreign key. What I want to achieve it -> create one more column with named position (int). And populate it like this: find all rows for each incident_id and and update each row with index or list of rows I get by each incident_id. exapmple:
incident_id 5 matches with 4 note rows so updated for the position will be 0, 1, 2, 3 accordingly. Ty
I would not recomment storing such derived information. Instead, you can create a view that uses row_number() to enumerate the rows of each incident_id:
create view myview as
select t.*, row_number() over(partition by incident_id order by id) - 1 rn
from mytable t
To get a stable result, you need a column that can be used to consistently order the rows of each incident: I called it id in the query. You can change that to the relevant column name (or set of columns) for your use case; you would typically use the primary key column(s) of your table.
EDIT
If you really wanted to materialize that value in a new column, and considering that the primary key of your table, you would do:
alter table mytable add column position int;
update mytable t
set position = t1.position
from (
select incident_note_id,
row_number() over(partition by incident_id order by incident_note_id) - 1 position
from mytable
) t1
where t1.incident_note_id = t.incident_note_id;

Using another table as value type

I have been searching this and can not find the proper answer if I need an JOIN or SUBQUERY, I have tried multiple ways if doing this and honestly I am hitting a major wall. I am trying to do something simple and I don't know how to progress
I have two tables I am trying to use: table 1) data 2) mapping
table 1 is like this the headers are :
Date
Value1
Value2
Value3
Value4
Etc.
Value in CSV style for example would be:
1/1/10,1,2,3,4
1/2/10,5,6,7,8
1/3/10,9,10,11,12
table 2 has only one row though, here are the headers and one row
Value1
Value2
Value3
Value4
The one row would be like:
Description1, Description2, Description3, Description4
So, I want to be able to, for example do a SELECT FROM table 1 and join in the Description for each matching row where the Column names are the same, so sample output based on the above would be to be like this:
1/1/10,1,Description1,2,Description2,3,Description3,4,Description4
1/2/10,5,Description1,6,Description2,7,Description3,8,Description4
Etc
Since there's just one row in table2 and no key, you can simply join it.
select *
from table1
join table2
Since there's just one row in table2 it's questionable why it exists at all. This could be done without a join at all.
select date, value1, 'Description1', value2, 'Description2', value3, 'Description3', value4, 'Description4'
from table1;
There's likely a better way to do this. Having columns like value1, value2, and value3 usually indicates poor table design. Instead of having four value columns, you should have four value rows. And instead of having a table with four columns of descriptions, it should be four rows of descriptions.
For example, let's say you're storing items in an order. An order can have many items. Rather than having a column for each item in an order like item1, item2, item3, you'd have a row for each item in a join table. Below that's order_items. Descriptions of the items is stored separately in its own table, one row per item.
user
----
id bigint primary key
name text not null
items
-----
id bigint primary key
name text not null
orders
------
id bigint primary key
user_id bigint references users(id)
created_at datetime
order_items
-----------
order_id bigint references orders(id)
item_id bigint references items(id)
If you want to look up all the items in an order, with their names, you'd use the order_items table to get all the items in an order, and join with the items table to get each item's name.
select i.name
from order_items oi
join items i on i.id = oi.item_id
where oi.order_id = ?
Comma separated lists of values are awkward to handle.
Rather than a CSV, storing the values in a mapping table is typically used.
If I understand correctly then I believe that the following may demonstrate along the lines of what you want:-
DROP TABLE IF EXISTS table1 /* Assigned Values per date (the mapping table)*/;
DROP TABLE If EXISTS table2 /* Values */;
CREATE TABLE IF NOT EXISTS table2 (valueid INTEGER PRIMARY KEY,value_description TEXT);
CREATE TABLE IF NOT EXISTS table1 (date TEXT, valueid_reference INTEGER REFERENCES table2(valueid),value INTEGER, UNIQUE(date, valueid_reference));
INSERT INTO table2 VALUES (1,'Value1 Description'),(2,'Value2 Description'),(3,'Value3 Description'),(4,'Value4 Description');
INSERT INTO table1 VALUES
('1/1/10',1,1),('1/1/10',2,2),('1/1/10',3,3),('1/1/10',4,4),
('1/2/10',1,5),('1/2/10',2,6),('1/2/10',3,7),('1/2/10',4,8)
;
SELECT date||','||group_concat(value||','||value_description) AS all_values_and_descriptions FROM table1 JOIN table2 ON valueid_reference = valueid GROUP BY date;
SELECT * FROM table1;
This results in :-
Noting that the REFRENCES (Foreign Key) will be a noop unless Foreign Key support is enabled. However, without it will still work.
As can be seen each value per date is an individual row in table 1 (so 4 rows per date). It is the group_concat function that is used to get all the values per date in conjunction with the GROUP BY clause which creates a set of rows (a Group) for each date.
The 2nd SELECT shows the rows in table1 :-

sql conversion script

I have a 2 databases that I want to merge with some similiar tables. The source tables have id as bigint and my destination table has int as ID. There aren't that many records in my source table (< 20k) so I want to assign new ids to all records so the ids can fit in an int. How can I do this with sql?
First Option
You can Use Sequence object as follow:
First Create a Sequence object and assign it's Start With value to max Id value in destination table plus 1. For example if max Id in destination table is 100, you need to assign 101 as Start With. You can also obtain the max Id value from destination table using a Max(Id) aggregate function and store it in a variable:
CREATE SEQUENCE SeqId
START WITH [Max value of Id in destination table]
INCREMENT BY 1 ;
GO
Then insert to destination table using following query:
Insert Into tblXXX (Id, ...) Values (NEXT VALUE FOR SeqId, ...)
Read more about Sequence Object
Second Option
You can make the destination table's Id column as Identity column with seed equal to destination table's Id column max value and Increment equal to 1.
Here is detailed example also Here
You did not provide much details so I can only provide a general guideline:
Note: Example assumes that you want to merge tables A and B into C and you want to generate new IDs. I also assume that these IDs are not referenced by other tables (foreign keys).
First you get record counts from tables A and B:
DECLARE #countA INT
DECLARE #countB INT
SET #countA = ( SELECT COUNT(*) FROM A )
SET #countB = ( SELECT COUNT(*) FROM B )
Next you use a window function to generate new IDs and insert records into table C.
INSERT INTO C
SELECT #countA + ROW_NUMBER() OVER( ORDER BY ID ) AS ID, ....
FROM A
INSERT INTO C
SELECT #countA + #countB + ROW_NUMBER() OVER( ORDER BY ID ) AS ID, ....
FROM B

Insert columnA values of Table 1 into another table if match occur

I have two tables.Table A has 4 columns. And table B has two columns.I want to insert value of one column of tabel A from one column of table B based on condtion if id matches.
how i can do this ? For example if [Movieid] in 1st table =[IMDBid] in second table then insert [count] of table 1=[CB] in table 2.
i want to do it once for full table.
[column] -> these are colums
i m using sql server.
Tabel 1 : Movieid,count,
Tabel 2: IMDBid, CB
Results which i want: i want to insert values of CB column in count where Movieid=IMDBid
Tabel 1 :
(Nick Id,MovieId,Rating,MovId)-> (1,4972,6.25,?)(1,24216,7.25,?)
Tabel 2 :
(Imdbid,Title,ImdbPyId,Id)-> (4972,hello,32450,1)(24216,hi,62450,2)
Insert /fill value of MovId(tabel1) using values Id(tabel2)where MovieId(tabel1)==Imdbid(tabel2)
You can do it like,
UPDATE tabl1
SET count = (SELECT CB FROM tabl2 WHERE IMDBid = Movieid)
But it is gonna blow up if you have multiple values returning from the subquery.
So make sure to use the appropriate function to get the single value from that subquery whichever meets your requirements.
If your tables have 1:1 relationship then it should be fine. But if it is 1:n then you need to use either aggragate functions or the TOP 1 clause in that subqyery.
solution is UPDATE tabl1
SET count = (SELECT CB FROM tabl2 WHERE IMDBid = Movieid)