SQL Concat Query - sql

I have two tables like this:
TABLE user(
id CHAR(100)
text TEXT
)
TABLE post(
postid CHAR(100)
postedby CHAR(100)
text TEXT
FOREIGN KEY (postedby) references user
);
I need a query that for each user concatenates the TEXT column of all posts of that user and put them in the text column of the user. the order is not important.
What should I do?

To select the values use GROUP_CONCAT:
SELECT postedby, GROUP_CONCAT(text)
FROM post
GROUP BY postedby
To update your original table you will need to join this result with your original table using a multi-table update.
UPDATE user
LEFT JOIN
(
SELECT postedby, GROUP_CONCAT(text) AS text
FROM post
GROUP BY postedby
) T1
ON user.id = T1.postedby
SET user.text = IFNULL(T1.text, '');

Related

PostgreSQL- insert result of query into exisiting table, auto-increment id

I have created an empty table with the following SQL statement. My understanding (based on this tutorial: https://www.postgresqltutorial.com/postgresql-tutorial/postgresql-serial/) was that SERIAL PRIMARY KEY will automatically provide an auto-incremented id for every new row:
CREATE TABLE "shema".my_table
(
id SERIAL PRIMARY KEY,
transaction text NOT NULL,
service_privider text NOT NULL,
customer_id text NOT NULL,
value numeric NOT NULL
)
WITH (
OIDS = FALSE
);
ALTER TABLE "shema".my_table
OWNER to admin;
Now I am querying another tables and would like to save the result of that query into my_table. The result of the query outputs following schema:
transaction
service_provider
customer_id
value
meaning the schema of my_table minus id. when I try to execute:
INSERT into my table
Select {here is the query}
Then I am getting an error that column "id" is of type integer but expression is of type text. I interpret it that the sql query is looking for id column and cannot find it. How can I insert data into my_table without explicitly stating id number but have this id auto-generated for every row?
Always mention the columns you want to INSERT:
INSERT INTO schemaname.my_table("transaction", service_privider, customer_id, value)
SELECT ?, ?, ?, ?;
If you don't, your code will break now or somewhere in the future.
By the way, transaction is a reserved word, try to use a better column name.

Query optimization: connecting meta data to a value list table

I have a database containing a table with data and a meta data table. I want to create a View that selects certain meta data belonging to an item and list it as a column.
The basic query for the view is: SELECT * FROM item. The item table is defined as:
CREATE TABLE item (
id INTEGER PRIMARY KEY AUTOINCREMENT
UNIQUE
NOT NULL,
traceid INTEGER REFERENCES trace (id)
NOT NULL,
freq BIGINT NOT NULL,
value REAL NOT NULL
);
The meta data to be added follow the schema "metadata.parameter='name'"
The meta table is defined as:
CREATE TABLE metadata (
id INTEGER PRIMARY KEY AUTOINCREMENT
UNIQUE
NOT NULL,
parameter STRING NOT NULL
COLLATE NOCASE,
value STRING NOT NULL
COLLATE NOCASE,
datasetid INTEGER NOT NULL
REFERENCES dataset (id),
traceid INTEGER REFERENCES trace (id),
itemid INTEGER REFERENCES item (id)
);
The "name" parameter should be selected this way:
if a record exists where parameter is "name" and itemid matches item.id, then its value should be included in the record.
otherwise, if a record exists where parameter is "name", "itemid" is NULL, and traceid matches item.traceid, its value should be used
otherwise, the result should be NULL, but the record from the item table should be included anyway
Currently, I use the following query to achieve this goal:
SELECT i.*,
COALESCE (
MAX(CASE WHEN m.parameter='name' THEN m.value END),
MAX(CASE WHEN m2.parameter='name' THEN m2.value END)
) AS itemname
FROM item i
JOIN metadata m
ON (m.itemid = i.id AND m.parameter='name')
JOIN metadata m2
ON (m2.itemid IS NULL AND m2.traceid = i.traceid AND m2.parameter='name')
GROUP BY i.id
This query however is somewhat inefficient, as the metadata table is used twice and contains many more records than just the "name" ones. So I am looking for a way to improve speed, especially regarding the case that some extensions are about to be implemented:
there is a third level "dataset" that should be included: a "parameter=name" should be used if it has the same datasetid as the item (will be looked up for the items by searching another which connects traceid and datasetid), if no "parameter=name" exists with either "itemid" matching or "traceid" matching
more meta data should be queried by the view following the same schema
Any help is appreciated.
First of all, you can use one join instead of 2, like this:
JOIN metadata m ON (m.parameter='name' AND (m.itemId = i.id OR (m.itemId IS NULL AND m.traceid = i.traceid)))
Then you can remove COALESCE, using simple select:
SELECT i.*, m.value as itemname
Result should look like this:
SELECT i.*, m.value as itemname
FROM item i
JOIN metadata m ON (m.parameter='name' AND (m.itemId = i.id OR (m.itemId IS NULL AND m.traceid = i.traceid)))
GROUP BY i.id

How can I fill empty table rows which references another table's column ID?

I want to fill out empty notification_settings for each user that already exists. IDs (PKs) are auto-generated by Hibernate in each table. Here is the user Table :
CREATE TABLE lottery_user (
id int8 not null,
email varchar(255) not null,
password varchar(255) not null,
notification_settings_id int8,
role varchar (50) default 'USER',
registry_date TIMESTAMP default now(),
primary key (id)
ADD CONSTRAINT FK_USER_TO_NOTIFICATION_SETTINGS
FOREIGN KEY (notification_settings_id) REFERENCES notification_settings
);
And here is the notification_settings table which I need to fill out for users that don't have it filled out for them.
CREATE TABLE notification_settings (
id int8 not NULL ,
test1_events bool DEFAULT TRUE ,
test2_events bool DEFAULT TRUE ,
test3_events bool DEFAULT TRUE ,
test4_events bool DEFAULT TRUE ,
PRIMARY KEY (id)
);
Basically, I need to use "INSERT INTO notification_settings (test1_events, test2_events, test3_events, test4_events) VALUES (True, True, True, True)" something similar to that. And of course, condition should be something like this "where these rows are empty for users". I can't seem to get Syntax right.
BIG NOTE: SQL code is for presentation purpose, so you can have an idea what kind of tables I have. I just need to get INSERT script right. Tables are working fine, just need to generate notification_settings values for users that already exist.
Another Note: Using Flyway, so it's not just about Hibernate. If that has to do with anything.
Are you just looking for:
INSERT INTO notification_settings (id)
SELECT id
FROM user
WHERE id NOT IN (SELECT id FROM notifiation_settings)
You might be looking to insert into an identity field:
SET IDENTITY_INSERT my_table ON
Since your foreign key constraint goes from notification_settings to user, the condition "where these rows are empty for user X" does not apply to your schema. On the other hand - "I want to fill out empty notification_settings for each user that already exists" can be done by using an insert...select construct:
set #rank=0
select #maxid = max(id) from notification_settings
insert into notification_settings (id)
select #maxid + #rank:=#rank+1 as rank
from user
where notification_settings_id is null
What is interesting is how you put those newly generated IDs back into the user table. Homework assignment for next time :)
INSERT INTO notification_settings (id)
SELECT u.id
FROM user u
WHERE
not exists (SELECT * FROM notifiation_settings ns where ns.id=i.id)
I will answer my own question how I dealt with it. Firstly, I insert IDs into notification_settings id, then I get those IDs and set them into lottery_user table's FK (notification_settings_id). Then I just delete unneeded IDs. Yea not perfect but it works.
INSERT INTO notification_settings (id) select lu.id from lottery_user lu where lu.id not in(select ns.id from notification_settings ns);
update lottery_user lu set notification_settings_id = (select ns.id from notification_settings ns where ns.id = lu.id) where lu.notification_settings_id is null;
delete from notification_settings ns where not exists (select * from lottery_user lu where lu.notification_settings_id = ns.id);
Also, script to Alter Sequence for new Lottery_user entities.
do $$
declare maxid int;
begin
select max(id) from lottery_user into maxid;
IF maxid IS NOT NULL THEN
EXECUTE 'ALTER SEQUENCE notification_settings_seq START with '|| maxid;
END IF;
end;
$$ language plpgsql;

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'

SQL Server: Extracting a Column Into a Table

I have a table with a column that I want to extract out and put into a separate table.
For example, lets say I have a table named Contacts. Contacts has a column named Name which stores a string. Now I want to pull out the names into another table named Name and link the Contact.Name column to the Id of the Name table.
I can only use SQL to do this. Any ideas on the best way to go about this?
Let me know if I can clarify anything, thanks!
[edit]
One problem is that different contacts can be tied to the same name. So when different contacts have the same name and it gets exported the Name table would only have one unique row for that name and all the contacts would point to that row. I guess this wouldn't make sense if I were actually working on a contact book, but I'm just using it to illustrate my problem.
CREATE TABLE Name (NameID int IDENTITY(1, 1), [Name] varchar(50))
INSERT INTO Name ([Name])
SELECT DISTINCT [Name]
FROM Contact
ALTER TABLE Contact
ADD COLUMN NameID int
UPDATE Contact
SET NameID = [Name].NameID
FROM Contact
INNER JOIN [Name]
ON Contact.[Name] = [Name].[Name]
ALTER TABLE Contact
DROP COLUMN [Name]
Then add foreign key constraint, etc.
Create the new table with a Foreign key that points back to the contact table. Then insert the names and contactids from the contact table into this new table. After that you can drop the "name" column from the contact table.
CREATE TABLE Name
(
ContactId int,
Name nvarchar(100)
);
INSERT Name(Name)
SELECT ContactId, Name From Contact;
ALTER TABLE Contact
DROP Column name;
EDIT: Since you have edited the question to mention that one name can be associated with multiple contacts, this changes things in the opposite way.
CREATE TABLE Name
(
NameId int IDENTITY,
Name nvarchar(100)
);
INSERT Name(Name)
SELECT DISTINCT Name From Contact;
ALTER TABLE Contact
ADD NameId int;
UPDATE c
SET c.NameId = n.NameId
FROM Contact c
JOIN Name n on n.Name = c.Name;
ALTER Table Contact
Drop Column Name;
NOTE: Make sure that you create the appropiate foreign key between the Contact and Name tables using the NameId on the Contact table and also create a UNIQUE constraint on the "name" column in the Name table.
insert into another_table( contact_id, name )
select id, name
from contacts;
insert into new_table (contact_id, name)
select min(id), name
from contacts
group by name;
This is one way of ensuring only one row per name - you can substitute other functions for min (like, for eg max).
I'm not too sure why you would want to do this, though. No matter what, you will end up with some contacts that don't have a name linked to them...
ALTER TABLE `Contacts` ADD `name_id` INT( 12 ) NOT NULL
ALTER TABLE `Name` ADD `Name` VARCHAR( 200 ) NOT NULL
INSERT INTO Name (id, name) SELECT id, Name FROM Contacts
ALTER TABLE `Contacts` DROP `Name`
The problem is the name_id field, which is filles with "0" and should be have the same value as the id in the Contacts-Table. Here you can use the LOOP or ITERATE statement (if you using MySQL).