I'm new to JSON/JSONB datatypes and I'm having some problems selecting the JSONB elements from deeper levels.
Here is an example table:
CREATE TABLE person (id integer, details jsonb);
INSERT INTO person (id, details) VALUES
("id": 1, {"favorites":{"colors":"blue", "colors":"red"}}),
("id": 2),
("id": 3, {"favorites":{"colors":"blue", "colors":"red", "colors":"green"}});
I would like to select all p.details ->'favorites' ->>'colors', for example:
------------------------
| id | Favorite colors |
------------------------
| 1 | blue |
------------------------
| 1 | red |
------------------------
| 3 | blue |
------------------------
| 3 | red |
------------------------
| 3 | green |
------------------------
The following (or similar) gives empty column for Favorite colors (tried also with jsonb_array_elements):
SELECT p.id, p.details ->'favorites' ->>'colors' AS "Favorite colors"
FROM "person" p;
SELECT p.id, json_array_elements((p.details ->'favorites' ->>'colors')::json) AS "Favorite colors"
FROM "person" p;
The following works with a simple query, but it crashes the browser when retrieving more data (the end users use the queries in a web browser).
SELECT p.id, ((json_array_elements((json_array_elements(p.details::json)::json->>'favorites')::json))::json->>'colors') AS "Favorite colors"
FROM "person" p;
Seems that the browser is consuming lot of memory. I've found some posts saying that Multiple calls to json_array_elements slow down the query: https://www.postgresql.org/message-id/52EEEC37.9040305#2ndquadrant.com
Any advice on this?
There is some syntax errors in sql queries you provided.
If I correctly guessed db schema than they should looks like this:
DROP TABLE person;
CREATE TABLE person (id integer, details jsonb);
INSERT INTO person (id, details) VALUES
(1, '{"favorites":[{"colors":"blue"}, {"colors":"red"}]}'::jsonb),
(2, '{}'::jsonb),
(3, '{"favorites":[{"colors":"blue"}, {"colors":"red"}, {"colors":"green"}]}'::jsonb);
If this is correct than you can get the results via:
select id, jsonb_array_elements(details->'favorites')->'colors' from person
Related
Assume we have the following table whose purpose is to autogenerate a numeric id for distinct (name, location) tuples:
CREATE TABLE mapping
(
id bigserial PRIMARY KEY,
name text NOT NULL,
location text NOT NULL,
);
CREATE UNIQUE INDEX idx_name_loc on mapping(name location)
What is the most efficient way to query for a set of (name, location) tuples and autocreate any mappings that don't already exist, with all mappings (including the ones we created) being returned to the user.
My naive implementation would be something like:
SELECT id, name, location
FROM mappings
WHERE (name, location) IN ((name_1, location_1)...(name_n, location_n))
do something with the results in a programming language of may choice to work out which results are missing.
INSERT
INTO mappings (name, location)
VALUES (missing_name_1, missing_loc_1), ... (missing_name_2, missing_loc_2)
ON CONFLICT DO NOTHING
This gets the job done but I get the feeling there's probably something that can a) be done in pure sql and b) is more efficient.
You can use DISTINCT to get all possible values for the two columns, and CROSS JOIN to get their Carthesian product.
LEFT JOIN with the original table to get the actual records (if any):
CREATE TABLE mapping
( id bigserial PRIMARY KEY
, name text NOT NULL
, location text NOT NULL
, UNIQUE (name, location)
);
INSERT INTO mapping(name, location) VALUES ('Alice', 'kitchen'), ('Bob', 'bedroom' );
SELECT * FROM mapping;
SELECT n.name, l.location, m.id
FROM (SELECT DISTINCT name from mapping) n
CROSS JOIN (SELECT DISTINCT location from mapping) l
LEFT JOIN mapping m ON m.name = n.name AND m.location = l.location
;
Results:
DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
INSERT 0 2
id | name | location
----+-------+----------
1 | Alice | kitchen
2 | Bob | bedroom
(2 rows)
name | location | id
-------+----------+----
Alice | kitchen | 1
Alice | bedroom |
Bob | kitchen |
Bob | bedroom | 2
(4 rows)
And if you want to physically INSERT the missing combinations:
INSERT INTO mapping(name, location)
SELECT n.name, l.location
FROM (SELECT DISTINCT name from mapping) n
CROSS JOIN (SELECT DISTINCT location from mapping) l
WHERE NOT EXISTS(
SELECT *
FROM mapping m
WHERE m.name = n.name AND m.location = l.location
)
;
SELECT * FROM mapping;
INSERT 0 2
id | name | location
----+-------+----------
1 | Alice | kitchen
2 | Bob | bedroom
3 | Alice | bedroom
4 | Bob | kitchen
(4 rows)
I am new to the SQL world. I would like to replace the Games.home_team_id and Games.away_team_id with the Corresponding entry in the Teams.name column.
First I start by initializing a small table of data:
CREATE TABLE Games (id,away_team_id INT,away_team_score INT,home_team_id INT, home_team_score INT);
CREATE TABLE
INSERT INTO Games (id,away_team_id,away_team_score,home_team_id,home_team_score)
VALUES
(1,1,1,2,4),
(2,1,3,3,2),
(3,1,1,4,1),
(4,2,0,3,2),
(5,2,3,4,1),
(6,3,5,4,2)
;
INSERT 0 6
Then I create a template of a reference table
CREATE TABLE Teams (id INT, name VARCHAR(63);
CREATE TABLE
INSERT INTO Teams (id, name)
VALUES
(1, 'Oogabooga FC'),
(2, 'FC Milawnchair'),
(3, 'Ron\'s Footy United'),
(4, 'Pylon City FC')
;
INSERT 0 4
I would like to have the table displayed as such:
| id | away_team_name | away_team_score | home_team_name | home_team_score |
-----+----------------+-----------------+----------------+------------------
| 1 | Oogabooga FC | 1 | FC Milawnchair | 4 |
...
I managed to get a join query to show the first value from Teams.name in the away_team_name field using this JOIN:
SELECT
Games.id,
Teams.name AS away_team_name,
Games.away_team_score,
Teams.name AS home_team_name,
Games.home_team_score
FROM Games
JOIN Teams ON Teams.id = Games.away_team_id
;
| id | away_team_name | away_team_score | home_team_name | home_team_score |
-----+----------------+-----------------+----------------+------------------
| 1 | Oogabooga FC | 1 | Oogabooga FC | 4 |
...
But now I am stuck when I call it twice as a JOIN it shows the error:
SELECT
Games.id,
Teams.name AS away_team_name,
Games.away_team_score,
Teams.name AS home_team_name,
Games.home_team_score
FROM Games
JOIN Teams ON Teams.id = Games.away_team_id
JOIN Teams ON Teams.id = Games.home_team_id
;
ERROR: table name "teams" specified more than once
How do you reference the same reference the same column of the same table twice for a join?
You need to specify an alias for at least one of the instances of the table; preferably both.
SELECT
Games.id,
Away.name AS away_team_name,
Games.away_team_score,
Home.name AS home_team_name,
Games.home_team_score
FROM Games
JOIN Teams AS Away ON Away.id = Games.away_team_id
JOIN Teams AS Home ON Home.id = Games.home_team_id
Explanation: As you are joining to the same table twice, the DBMS (in your case, PostgreSQL) is unable to identify which of the tables you're referencing to when using its fields; the way to solve this is to assign an alias to the joined tables the same way you assign aliases for your columns. This way you can specify which of the joined instances are you referencing to in your SELECT, JOIN and WHERE statements.
I have two tables
Entity
House
In house table, there is a column entity_id of type text.
And entity_id will store multiple house ids.
So it will look like
entity_id (text)
------------------
[1,2]
[3,6]
Now I have to join this entity table with house table.
How will I achieve this.
I know this may not be a good design. Though now this my responsibility to do this.
This should work:
CREATE TABLE entity(
id integer,
entity_id integer[],
details text
);
CREATE TABLE houses(
id integer,
house_name text
);
INSERT INTO entity(id, entity_id, details)
VALUES
(1, '{1,3}', 'Left side houses'),
(2, '{2,4}', 'Right side houses');
INSERT INTO houses(id, house_name)
VALUES
(1, 'Left 1'),
(2, 'Right 1'),
(3, 'Left 2'),
(4, 'Right 2');
----------------------------------------------
SELECT h.id as house_id, h.house_name, e.details
FROM houses h
LEFT JOIN entity e
ON h.id = ANY(e.entity_id);
| house_id | house_name | details |
| -------- | ---------- | ----------------- |
| 1 | Left 1 | Left side houses |
| 2 | Right 1 | Right side houses |
| 3 | Left 2 | Left side houses |
| 4 | Right 2 | Right side houses |
As Laurenz says in comment: it seems to be an error of modelisation here.
So I will not directly answer to the question but I will give a most correct structure :
CREATE TABLE entity (id int primary key);
CREATE TABLE house (id int primary key);
CREATE TABLE entity_house (id bigserial, identity int references entity(id), idhouse int references house(id));
INSERT INTO entity VALUES (1), (2);
INSERT INTO house VALUES (3), (56);
INSERT INTO entity_house (identity, idhouse) VALUES (1,56), (2,56);
SELECT e.*, h.*
FROM entity_house eh
INNER JOIN entity e ON eh.identity = e.id
INNER JOIN house h ON eh.idhouse = h.id;
In try to insert value from a hstore (postgreql) to a more generic table
In my car table, I have theses fields
id
fields (hstore)
My store table, I have theses fields
id
key
value
car_id
date
How to loop to my fields property in insert key, value to my store table.
Is there a way to do it with a select command?
Example data:
insert into car values
(1, 'brand=>ford, color=>yellow'),
(2, 'brand=>volvo, mileage=>50000, year=>2015');
Use the function each(hstore) to get pairs (key, value) of hstore column:
select id, key, value
from car, each(fields);
id | key | value
----+---------+--------
1 | brand | ford
1 | color | yellow
2 | year | 2015
2 | brand | volvo
2 | mileage | 50000
(5 rows)
The insert command may look like this:
insert into store (car_id, key, value)
select id, key, value
from car, each(fields);
I have two tables, described as below.
Table 1: countries
c_id, int
c_name, varchar(20) (PK)
Sample records in this table are.
c_id | c_name
1 | USA
2 | UK
3 | PAK
Table 2: immigrants
i_id, int
i_name, varchar(20)
i_country, int (FK)
Sample records in this table are.
i_id | i_name | i_country
1 | John | 1
2 | Graham | 2
3 | Ali | 3
Question 1:
I want to create two classes (tables) in OrientDB but I need to know what should be the data type of the FK field and what to insert in it. I mean what should I write in query to insert the id of the PK table. Does it need to be #rid? How?
QUESTION 2:
What is the OrientDB SQL for producing the following output.
i_id | i_name | i_country | c_id | c_name
1 | John | 1 | 1 | USA
2 | Graham | 2 | UK
3 | Ali 3 | PAK
with OrientDB you can avoid the creation of FK fields and join operations by using direct Edge links between records. For example:
create class Country extends V;
create class Immigrant extends V;
create class comesFrom extends E;
create property Country.c_id integer;
create property Country.c_name String;
create property Immigrant.i_id integer;
create property Immigrant.i_name String;
insert into Country(c_id, c_name) values (1, USA);
insert into Country(c_id, c_name) values (2, UK);
insert into Country(c_id, c_name) values (3, PAK);
insert into Immigrant(i_id, i_name) values (1, John);
insert into Immigrant(i_id, i_name) values (2, Graham);
insert into Immigrant(i_id, i_name) values (3, Ali);
Now you can connect directly the record you want (I used an Edge called 'comesFrom' and subqueries to link the id fields but you could also directly use the #RID field)
create edge comesFrom from (select from Immigrant where i_id = 1) to (select from Country where c_id = 1);
create edge comesFrom from (select from Immigrant where i_id = 2) to (select from Country where c_id = 2);
create edge comesFrom from (select from Immigrant where i_id = 3) to (select from Country where c_id = 3);
Finally you can query the fields you want without join operations:
select i_id, i_name, out('comesFrom').c_id as c_id, out('comesFrom').c_name as c_name
from Immigrant unwind c_id, c_name
----+------+----+------+----+------
# |#CLASS|i_id|i_name|c_id|c_name
----+------+----+------+----+------
0 |null |1 |John |1 |USA
1 |null |2 |Graham|2 |UK
2 |null |3 |Ali |3 |PAK
----+------+----+------+----+------
In Orient Studio you should obtain a graph like this:
OrientDB does not support join.
You should declare the i_country field in class Immigrant as Link to Country class, or you can use Graph Model if you want bidirectional relationship.
If you want to use Direct link
you can insert immigrants like this
insert into Immigrant set id=1, name ='John', country = (select from country where id = 1 )
then
select id,name,country.id,country.name form Immigrant
you could speed up the insertion and link operations with a javascript function like this:
so the edge is automatically created when you decide to create a new person. You can also decide where to place the person:
Could it be helpful for you ?
I think that you have created a Document DB because I reproduced the issue by creating a Document DB and not a Graph DB and I get, like you, the same exception. This is because if you want to work with Vertices and Edges you must use the Graph DB type.