how to get data on arrays of values - sql

I have pairs of values test_name, test_surname.
How can I delete these rows from the table with one query. I assumed it could be done this way, but it can't be done this way.
DELETE FROM test_info
WHERE id_name = ($1::uuid[])
AND id_surname = ($2::uuid[])
this is schema
create table test_info
(
id_name uuid not null,
id_surname uuid not null,
);

Example of unnesting 2 arrays in one query (arrays must have the same size and dimension)
select unnest(array['1','2']),unnest(array['3','4']);
Delete rows
delete from test_info
where
(id_name,id_surname) in (select unnest($1::uuid[]),unnest($2::uuid[]))

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.

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 :-

Insert data from one table to other using select statement and avoid duplicate data

Database: Oracle
I want to insert data from table 1 to table 2 but the catch is, primary key of table 2 is the combination of first 4 letters and last 4 numbers of the primary key of table 1.
For example:
Table 1 - primary key : abcd12349887/abcd22339887/abcder019987
In this case even if the primary key of table 1 is different, but when I extract the 1st 4 and last 4 chars, the output will be same abcd9887
So, when I use select to insert data, I get error of duplicate PK in table 2.
What I want is if the data of the PK is already present then don't add that record.
Here's my complete stored procedure:
INSERT INTO CPIPRODUCTFAMILIE
(productfamilieid, rapport, mesh, mesh_uitbreiding, productlabelid)
(SELECT DISTINCT (CONCAT(SUBSTR(p.productnummer,1,4),SUBSTR(p.productnummer,8,4)))
productnummer,
ps.rapport, ps.mesh, ps.mesh_uitbreiding, ps.productlabelid
FROM productspecificatie ps, productgroep pg,
product p left join cpiproductfamilie cpf
on (CONCAT(SUBSTR(p.productnummer,1,4),SUBSTR(p.productnummer,8,4))) = cpf.productfamilieid
WHERE p.productnummer = ps.productnummer
AND p.productgroepid = pg.productgroepid
AND cpf.productfamilieid IS NULL
AND pg.productietype = 'P'
**AND p.ROWID IN (SELECT MAX(ROWID) FROM product
GROUP BY (CONCAT(SUBSTR(productnummer,1,4),SUBSTR(productnummer,8,4))))**
AND (CONCAT(SUBSTR(p.productnummer,1,2),SUBSTR(p.productnummer,8,4))) not in
(select productfamilieid from cpiproductfamilie));
The highlighted section seems to be wrong, and because of this the data is not picking up.
Please help
Try using this.
p.productnummer IN (SELECT MAX(productnummer) FROM product
GROUP BY (CONCAT(SUBSTR(productnummer,1,4),SUBSTR(productnummer,8,4))))

Why does tsql Rand function not work in where clause?

I am trying to select a single row at random from a table. I am curious as to why the two statements below don't work:
select LastName from DataGeneratorNameLast where id = (ABS(CHECKSUM(NewId())) % 3)+1
select LastName from DataGeneratorNameLast where id = cast(Ceiling(RAND(convert(varbinary, newid())) *4) as int)
Both statements return, at random, either 1 row, no rows, or multiple rows. For the life of me I can't figure out why. Just adding top 1 to the query only solves the problem of multiple rows - but not of no rows returned.
Yes I could do the same thing by selecting top 1 and ordering by newid(). But the mystery of why this does not work is driving me crazy.
Thoughts on why I get multiple rows back?
Here is the table I am using to select from:
Create Table dbo.DataGeneratorNameLast
(
[Id] [int] IDENTITY(1,1) NOT NULL,
LastName varchar(50) NOT NULL,
)
Go
insert into DataGeneratorNameLast (LastName) values ('SMITH')
insert into DataGeneratorNameLast (LastName) values ('JOHNSON')
insert into DataGeneratorNameLast (LastName) values ('Booger')
insert into DataGeneratorNameLast (LastName) values ('Tiger')
The newid() gets evaluated for every row it is compared against, generating a different number. To do what you want, you should generate the random value into a variable before the select and then reference the variable.
Declare #randId int = (abs(checksum(newid())) % 3) + 1;
select LastName from DataGeneratorNameLast where id = #randId;
As Martin said in comments to this. Rand() would behave differently, only being evaluated once per query.
If the table has at least one row that this query would return one row is mandatory.
select TOP (1) LastName
from DataGeneratorNameLast
ORDER BY NEWID()
Notice that this solution can be slow if the table has a large number of rows.
About select LastName from DataGeneratorNameLast where id = #Rand - This solution does not guarantee that there exists a row with id. Even the IDENTITY column can contain gaps. If you definitely need one row then do a preliminary check IF EXISTS (select * from DataGeneratorNameLast where id = #Rand) SELECT ...
I've Had a similar issue and fixed it by making the ID a PRIMARY KEY.
NEWID() is computed per-row. Without a primary key, there is no access pattern other than a table scan, and the filter is checked for each row, so a different value is computed for each row, and you get however many rows match.
With the key, a seek is available, so the predicate is computed once and used as a search argument for a seek.

SQL Insert rows into table that must have 2 distinct columns but also one non distinct column

I have two tables, a promotion table and a prize table.
CREATE TABLE PRIZE(
PRIZEID INT NOT NULL PRIMARY KEY,
COST DOUBLE NOT NULL,
PRIZENAME VARCHAR(100) NOT NULL,
)
CREATE TABLE PROMOTION (
PROMOTIONID INTEGER NOT NULL,
LEVEL INTEGER NOT NULL,
AMOUNT NOT NULL,
COST DOUBLE NOT NULL DEFAULT 0,
PRIZENAME VARCHAR(100) NOT NULL DEFAULT ' ',
PRIZEID INTEGER
)
The prize table is currently empty and I want to copy records from the promotion table into the prize table. in doing this I only want to select those records that have unique values in two columns, the prizename and the cost. I will be dropping those columns from promotion afterwards.
Right now I have the following sql statement
INSERT INTO prize(PRIZEID, COST, PRIZENAME)
SELECT r.PRIZEID, r.COST, r.PRIZENAME
FROM PROMOTION r;
but this will insert all records into the prize table. I realize I can use the distinct keyword in the select to select unique instances of cost and prizename but in my case the prizeid will always be unique and since the distinct keyword applies to all columns in the select clause it won't help.
Thanks in advance!
You'll have to just replace the PRIZEID value with new ones. Because it sounds like you currently have duplicates on your PROMOTION table
First add all the distinct PRIZENAMEs and COSTs to your new PRIZE table:
INSERT INTO prize(PRIZEID, COST, PRIZENAME)
SELECT DISTINCT (SELECT MAX(PRIZEID)+1 FROM PRIZE), r.COST, r.PRIZENAME
FROM PROMOTION r;
Then update your PROMOTIONs table with the new PRIZEID
UPDATE PROMOTION R
SET R.PRIZEID =
(SELECT P.PRIZEID
FROM PRIZE
WHERE P.PRIZENAME=R.PRIZENAME
AND P.COST=R.COST);
Then, I think from there you can safely delete the columns from your PROMOTIONs table
This will get the unique prizes based on prizename and cost and get the lowest prizeid for the group.
INSERT INTO prize(PRIZEID, COST, PRIZENAME)
SELECT MIN(r.PRIZEID), r.COST, r.PRIZENAME
FROM PROMOTION r
GROUP BY r.COST, r.PRIZENAME
;