Postgresql easiest way to replace ID's with Names - postgresql

In Postgresql:
Say I have a table Dogs:
SELECT * FROM "Dogs"
...
Name OwnerID
'Pooch' 'OWR14'
'Sparky' 'OWR12'
'Pooch' 'OWR17'
And a table Owners:
SELECT * FROM "Owners"
...
ID Name
'OWR14' 'Peter'
'OWR12' 'Jack'
'OWR17' 'Mary'
What is the fastest/easiest way to replace the OwnerID field with the Name values from the Owner table?
My fastest way:
SELECT "Dogs"."Name", "Owners"."Name" AS "Owner"
FROM "Dogs"
LEFT JOIN "Owners" ON "Dogs"."OwnerID" = "Owners"."ID"
...
Name Owner
'Pooch' 'Peter'
'Sparky' 'Jack'
'Pooch' 'Mary'

looking to your sample could be an upodate with join
UPDATE Dogs
SET Dogs.OwnerID = Owners.Name
FROM Owners
WHERE Dogs.OwnerID = Owners.id;
Otherwise if you just need a faster query then be sure you have proper composite index on table Dogs
CREATE INDEX my_idx1 on "Dogs" ("OwnerID", "Name" )
and for table Owners
CREATE INDEX my_idx2 on "Owners" ("ID", "Name" )

Related

Renaming all existing attributes in an array of Json objects

I work with Postgres db.
For example - there is a table
CREATE TABLE public.json_objects(id serial primary key, objects text);
it stores such an arrays of json
INSERT INTO public.json_objects (objects) VALUES ('[{"name":"Ivan"}]'), ('[{"name":"Petr"}, "surname":"Petrov"}]'), ('[{"form":"OOO"}, {"city":"Kizema"}]');
How can I replace the attribute "name" with "first name" or "surname" with "second name" everywhere?
I am using update with the select - subquery.
In this case, a replacement will occur, but if the attribute does not exist in the json object, then it will be added to the json with a null value (and this should not be)
WITH updated_table AS (SELECT id, jsonb_agg(new_field_json) as new_fields_json
FROM (SELECT id, jsonb_array_elements(json_objects.objects::jsonb) - 'name' || jsonb_build_object('first name', jsonb_array_elements(json_objects.objects::jsonb) -> 'name') new_field_json FROM public.json_objects) r group by id) UPDATE public.json_objects SET objects = updated_table.new_fields_json FROM updated_table where json_objects.id = updated_table.id
This seems to be a single operation, so you can just use the regexp_replace function to replace the key
update table_1 set objects = regexp_replace(objects, '(\"name"+)', '"first name"');
update table_1 set objects = regexp_replace(objects, '(\"surname"+)', '"second name"')
Demo in dbfiddle

PostgreSQL query for tvshows with multiple genres

I'm trying to create a database of tvshows that I have personally watched.
I have 3 tables:
tvshow_list: id, title, episodes, year
genres: id, genre name
genres_in_tvshows: genreid, tvshow id
I want to have a list which would show the tvshow id, title, episodes, and the genres. I cannot find a psql query that shows me how to 1. "group_concat" the genres so more than one shows and 2. join the tables
First your genres_in_tvshows table should have foreign keys referencing table tvshows and genres :
create table genres_in_tvshows
( genre_id integer, tvshow_id integer
, constraint pk primary key (genre_id, tvshow_id)
, constraint fk_tv foreign key (tvshow_id) references tvshow_list(id)
, constraint fk_ge foreign key (genre_id) references genres(id)) ;
Then you can use the following query :
SELECT tv.*, ge.*
FROM tvshow_list AS tv
INNER JOIN genres_in_tvshows AS gt
ON gt.tvshow_id = tv.id
INNER JOIN genres AS ge
ON ge.id = gt.genre_id

Postgresql Null value populates left table when doing a left join. Why is this happening?

When I make the query:
"select * from asset a where (a.login_id = $1) and (a.status = 'pub')";
I get back the expected result:
{
id: 23f8jfj8gh2,
name: 'Asset1',
status: 'pub',
create_date: 2020-11-12T07:00:00.000Z,
...
}
But when I add a left join onto the query like this:
"select * from asset a left join asset_image i on (a.id = i.asset_id) where (a.login_id = $1) and (a.status = 'pub')";
I get back:
{
id: null, <---- THIS NULL VALUE for the asset id
name: 'Asset1',
status: 'pub',
create_date: 2020-11-12T07:00:00.000Z,
...
asset_id: null,
orig_name: null,
mimetype: null,
created: null
}
I am new to SQL, and I have looked at the docs for left joins, but I can't seem to figure out exactly where this is going wrong. Any help would be much appreciated! Thanks.
If the asset_image table contains its own column with the name of id when your query gets no match between (a.id = i.asset_id) it will overwrite the id field with null.
You may need to define each tables id column with a more unique name eg.
asset_id to replace id in the asset table
image_id to replace id in the asset_image table
Your problem is probably the * in SELECT *, which is something that you should always avoid in code.
If the table asset_image also has a column named id, your result set will contain two columns named id. You are probably looking at the wrong one.
Using SELECT * gives you no control over the columns you get in the result set, their name and their order. You should use an explicit column list and give columns with the same name in both tables an alias that allows you to disambiguate them.

Deleting a jsonb array item by name

I have the following table
CREATE TABLE country (
id INTEGER NOT NULL PRIMARY KEY ,
name VARCHAR(50),
extra_info JSONB
);
INSERT INTO country(id,extra_info)
VALUES (1, '{ "name" : "France", "population" : "65000000", "flag_colours": ["red", "blue","white"]}');
INSERT INTO country(id,extra_info)
VALUES (2, '{ "name": "Spain", "population" : "47000000", "borders": ["Portugal", "France"] }');
and i can add an element to the array like this
UPDATE country SET extra_info = jsonb_set(extra_info, '{flag_colours,999999999}', '"green"', true);
and update like this
UPDATE country SET extra_info = jsonb_set(extra_info, '{flag_colours,0}', '"yellow"');
I now would like to delete an array item with a known index or name.
How would i delete a flag_color element by index or by name?
Update
Delete by index
UPDATE country SET extra_info = extra_info #- '{flag_colours,-1}'
How can i delete by name?
As Arrays do not have direct access to items in a straightforward way, we can try to approach this differently through unnesting -> filtering elements -> stitching things back together. I have formulated a code example with ordered comments to help.
CREATE TABLE new_country AS
-- 4. Return a new array (for immutability) that contains the new desired set of colors
SELECT id, name, jsonb_set(extra_info, '{flag_colours}', new_colors, FALSE)
FROM country
-- 3. Use Lateral join to apply this to every row
LEFT JOIN LATERAL (
-- 1. First unnest the desired elements from the Json array as text (to enable filtering)
WITH prep AS (SELECT jsonb_array_elements_text(extra_info -> 'flag_colours') colors FROM country)
SELECT jsonb_agg(colors) new_colors -- 2. Form a new jsonb array after filtering
FROM prep
WHERE colors <> 'red') lat ON TRUE;
In the case you would like to update only the affected column without recreating the main table, you can:
UPDATE country
SET extra_info=new_extra_info
FROM new_country
WHERE country.id = new_country.id;
I have broken it down to two queries to improve readability; however you can also use a subquery instead of creating a new table (new_country).
With the subquery, it should look like:
UPDATE country
SET extra_info=new_extra_info
FROM (SELECT id, name, jsonb_set(extra_info, '{flag_colours}', new_colors, FALSE) new_extra_info
FROM country
-- 3. Use Lateral join to scale this across tables
LEFT JOIN LATERAL (
-- 1. First unnest the desired elements from the Json array as text (to enable filtering)
WITH prep AS (SELECT jsonb_array_elements_text(extra_info -> 'flag_colours') colors FROM country)
SELECT jsonb_agg(colors) new_colors -- 2. Form a new jsonb array after filtering
FROM prep
WHERE colors <> 'red') lat ON TRUE) new_country
WHERE country.id = new_country.id;
Additionally, you may filter rows via (As of PostgreSQL 9.4):
SELECT *
FROM country
WHERE (extra_info -> 'flag_colours') ? 'red'
Actually PG12 allows to do it without JOIN LATERAL:
SELECT jsonb_path_query_array(j #> '{flag_colours}', '$[*] ? (# != "red")'),
jsonb_set(j, '{flag_colours}', jsonb_path_query_array(j #> '{flag_colours}', '$[*] ? (# != "red")'))
FROM (SELECT '{ "name" : "France", "population" : "65000000",
"flag_colours": ["red", "blue","white"]}'::jsonb AS j
) AS j
WHERE j #? '$.flag_colours[*] ? (# == "red")';
jsonb_path_query_array | jsonb_set
------------------------+---------------------------------------------------------------------------------
["blue", "white"] | {"name": "France", "population": "65000000", "flag_colours": ["blue", "white"]}
(1 row)

How to left join in query builder without entity in doctrine zend

Hi I have M:M relationship between two tables, Contacts and Tags and their M:M table is called Contacts_Tags:
Contacts
------------
ID
Name
Tags
-----------
ID
Name
Contacts_Tags
--------------
Contact_ID
Tag_ID
I have entities for Contacts called Contact and for Tags called Tag but not for Contacts_Tags table.
I want to left join in query builder
$queryBuilder = $this->entityManager->getRepository(Contact::class)->createQueryBuilder("o")->select("o");
$queryBuilder->leftJoin(//here, "et", "WITH", "et.Contact_ID = o.ID")
->leftJoin(Tag::class, "t", "WITH", "t.ID = et.Tag_ID")
;
But I cannot figure out how to add it. I tried documentations but it says to add Entity when I add entity of ContactTag it throws error that entity should have primary key.
Any idea?
To do left join :
$queryBuilder->join(table, condition, columns, $queryBuilder::JOIN_LEFT);
with :
table is the name of a table or another Select or array [alias => table]
condition is a string (the same as in Sql language)
columns is an array like [alias => column_name, ...] or [column_name, ...], may be empty
$queryBuilder is a Select, can be replaced by \Zend\Sql\Select::JOIN_LEFT