postgreSQL hstore if contains value - sql

Is there a way to check if a value already exists in the hstore in the query itself.
I have to store various values per row ( each row is an "item").
I need to be able to check if the id already exists in database in one of the hstore rows without selecting everything first and doing loops etc in php.
hstore seems to be the only data type that offers something like that and also allows you to select the column for that row into an array.
Hstore may not be the best data type to store data like that but there isn't anything else better available.
The whole project uses 9.2 and i cannot change that - json is in 9.3.

The exist() function tests for the existence of a key. To determine whether the key '42' exists anywhere in the hstore . . .
select *
from (select test_id, exist(test_hs, '42') key_exists
from test) x
where key_exists = true;
test_id key_exists
--
2 t
The svals() function returns values as a set. You can query the result to determine whether a particular value exists.
select *
from (select test_id, svals(test_hs) vals
from test) x
where vals = 'Wibble';
hstore Operators and Functions
create table test (
test_id serial primary key,
test_hs hstore not null
);
insert into test (test_hs) values (hstore('a', 'b'));
insert into test (test_hs) values (hstore('42', 'Wibble'));

Related

Making an array of keys from json in postgresSQL

I have column "elements" in table which is having a json(array json) row values which looks like this
elements
[{"key":12,"value":"qw"},{"key":13,"value":"fa"}]
[{"key":32,"value":"24"},{"key":321,"value":"21"}]
I want to make an column of arrays for every row which consist of keys extracted from that row's json values ,my desired column "result" may look like this
elements
result
[{"key":12,"value":"qw"},{"key":13,"value":"fa"}]
{12,13}
[{"key":32,"value":"24"},{"key":321,"value":"21"}]
{32,321}
is there a way to do it? thank you
Schema (PostgreSQL v13)
CREATE TABLE test (
elements json
);
INSERT INTO test VALUES ('[{"key":12,"value":"qw"},{"key":13,"value":"fa"}]');
INSERT INTO test VALUES ('[{"key":32,"value":"24"},{"key":321,"value":"21"}]');
Query #1
select elements::text, array_agg(cast(value->>'key' as integer)) as result
from test, json_array_elements(elements)
group by 1
ORDER BY 1;
elements
result
[{"key":12,"value":"qw"},{"key":13,"value":"fa"}]
12,13
[{"key":32,"value":"24"},{"key":321,"value":"21"}]
32,321
View on DB Fiddle
select elements::text,
array_agg(value->>'key')
from your_table, json_array_elements(elements)
group by 1;

Filter an SQL Array text[] for matching value containing a parameter

I have a table with a TEXT[] column. I want to return all rows that have at least one of the array value that contains my parameter.
Right now I'm doing WHERE array_to_string(arr, ',') ilike '%myString%'
But I feel their must be a better optimized way of doing that kind of search.
Plus I would also like to search for values begining or ending by my parameter.
CREATE TABLE IF NOT EXISTS my_table
(
id BIGSERIAL,
col_array TEXT[],
CONSTRAINT my_table_pkey PRIMARY KEY (id)
)
insert into my_table(col_array)
VALUES ('{ABC,DEF}'),
('{FGH,IJK}'),
('{LMN}'),
('{OPQ}');
select * from my_table where ARRAY_TO_STRING(col_array, ',') ilike '%F%';
this works as it returns only first 2 rows.
You can find a sqlfiddle here: http://sqlfiddle.com/#!17/09632/7
I would use a sub-query:
select t.*
from my_table t
where exists (select *
from unnest(t.col_array) as x(e)
where x.e ilike '%F%')
You might want to re-consider your decision to de-normalize your model.
Quote from the manual
Arrays are not sets; searching for specific array elements can be a sign of database misdesign. Consider using a separate table with a row for each item that would be an array element. This will be easier to search, and is likely to scale better for a large number of elements.

Postgres: SELECT or INSERT in high concurrent write load DB

We have a DB for which we need a "selsert" (not upsert) function.
The function should take a text value and return a id column of existing row (SELECT) or insert the value and return id of new row (INSERT).
There are multiple processes that will need to perform this functionality (selsert)
I have been experimenting with pg_advisory_lock and ON CONFLICT clause for INSERT but am still not sure what approach would work best (even when looking at some of the other answers).
So far I have come up with following
WITH
selected AS (
SELECT id FROM test.body_parts WHERE (lower(trim(part))) = lower(trim('finger')) LIMIT 1
),
inserted AS (
INSERT INTO test.body_parts (part)
SELECT trim('finger')
WHERE NOT EXISTS ( SELECT * FROM selected )
-- ON CONFLICT (lower(trim(part))) DO NOTHING -- not sure if this is needed
RETURNING id
)
SELECT id, 'inserted' FROM inserted
UNION
SELECT id, 'selected' FROM selected
Will above query (within function) insure consistency in high
concurrency write workloads?
Are there any other issues I must consider (locking?, etc, etc)
BTW, I can insure that there are no duplicate values of (part) by creating unique index. That is not an issue. What I am after is that SELECT returns existing value if another process does INSERT (I hope I am explaining this right)
Unique index would have following definition
CREATE UNIQUE INDEX body_parts_part_ux
ON test.body_parts
USING btree
(lower(trim(part)));

JOOQ: extract value from Field

I have a table with PK and another column for other id. In some cases i need to insert record with equal values in both columns. For primary key values i'm using sequence, which gives a Field<Long> from Sequences.MY_SEQ.nextval().
How can i extract value from a Field<Long> for guaranteed insert same ids in both columns? Using Field<Long> in insert clause generates 2 different ids in columns.
Here is the solution:
Long id = dsl.select(Sequences.MY_SEQ.nextval()).fetchOne().value1();
Your own solution works, of course, but it will generate two round trips to the database. One for fetching the sequence value and another one for the insert. If that's not a problem, perfect. Otherwise, you can still do it in one single query using INSERT .. SELECT:
In SQL:
(using Oracle syntax. Your SQL syntax may vary...)
INSERT INTO my_table (col1, col2, val)
SELECT t.id, t.id, 'abc'
FROM (
SELECT my_seq.nextval AS id
FROM dual
) t
With jOOQ
Table<?> t = table(select(MY_SEQ.nextval().as("id"))).as("t");
dsl.insertInto(MY_TABLE)
.columns(MY_TABLE.COL1, MY_TABLE.COL2, MY_TABLE.VAL)
.select(
select(t.field("id"), t.field("id"), val("abc"))
.from(t))
.execute();

Sqlite - SELECT or INSERT and SELECT in one statement

I'm trying to avoid writing separate SQL queries to achieve the following scenario:
I have a Table called Values:
Values:
id INT (PK)
data TEXT
I would like to check if certain data exists in the table, and if it does, return its id, otherwise insert it and return its id.
The (very) naive way would be:
select id from Values where data = "SOME_DATA";
if id is not null, take it.
if id is null then:
insert into Values(data) values("SOME_DATA");
and then select it again to see its id or use the returned id.
I am trying to make the above functionality in one line.
I think I'm getting close, but I couldn't make it yet:
So far I got this:
select id from Values where data=(COALESCE((select data from Values where data="SOME_DATA"), (insert into Values(data) values("SOME_DATA"));
I'm trying to take advantage of the fact that the second select will return null and then the second argument to COALESCE will be returned. No success so far. What am I missing?
Your command does not work because in SQL, INSERT does not return a value.
If you have a unique constraint/index on the data column, you can use that to prevent duplicates if you blindly insert the value; this uses SQLite's INSERT OR IGNORE extension:
INSERT OR IGNORE INTO "Values"(data) VALUES('SOME_DATE');
SELECT id FROM "Values" WHERE data = 'SOME_DATA';