Extracting XML element value from Postgres - postgresql

Given:
CREATE TABLE xmltest(xtxt xml);
And:
INSERT INTO xmltest values ('<EMP><NAME>Mike</NAME><HIREDATE>12-FEB-96</HIREDATE></EMP><EMP><NAME>Bob</NAME><HIREDATE>13-AUG-97</HIREDATE></EMP><EMP><NAME>Paul</NAME><HIREDATE>17-JUN-94</HIREDATE></EMP><EMP><NAME>Jim</NAME><HIREDATE>01-JUN-94</HIREDATE></EMP>');
Using the base functionality of Postgres 9.2, how would I write a SELECT statement that returned only the employee names, 1 name per row in the result set? Or would I have to write a function in PL/PGSQL to do that?

You can extract fields of interest into an array using the xpath function, and then from there you can use the unnest builtin to split this array into multiple rows:
SELECT unnest(xpath('//name', xtxt))
FROM xmltest;
(Slightly borrowed from this question)

Related

How can I map jsonb value into a new array in PostgreSQL?

I'm fairly new to PostgreSQL specially to the whole set of functions it has to manage JSONs. I have this json example where I can grab always one element but, I want to map it to a new array:
select '{"a": {"b":{"c": [{"id":"foo"},{"id":"fuu"}]}}}'::json#>'{a,b,c}';
column
-----------------------------
[{"id":"foo"},{"id":"fuu"}]
(1 row)
What I really want is:
[foo, fuu]
If you are using Postgres 12 or newer you can use a JSON path query:
select jsonb_path_query_array(the_column, '$.a.b.c[*].id')
from the_table
This assumes that the column is a jsonb column (which it should be). If it's not you need to cast it: the_column::jsonb

PostgreSQL, allow to filter by not existing fields

I'm using a PostgreSQL with a Go driver. Sometimes I need to query not existing fields, just to check - maybe something exists in a DB. Before querying I can't tell whether that field exists. Example:
where size=10 or length=10
By default I get an error column "length" does not exist, however, the size column could exist and I could get some results.
Is it possible to handle such cases to return what is possible?
EDIT:
Yes, I could get all the existing columns first. But the initial queries can be rather complex and not created by me directly, I can only modify them.
That means the query can be simple like the previous example and can be much more complex like this:
WHERE size=10 OR (length=10 AND n='example') OR (c BETWEEN 1 and 5 AND p='Mars')
If missing columns are length and c - does that mean I have to parse the SQL, split it by OR (or other operators), check every part of the query, then remove any part with missing columns - and in the end to generate a new SQL query?
Any easier way?
I would try to check within information schema first
"select column_name from INFORMATION_SCHEMA.COLUMNS where table_name ='table_name';"
And then based on result do query
Why don't you get a list of columns that are in the table first? Like this
select column_name
from information_schema.columns
where table_name = 'table_name' and (column_name = 'size' or column_name = 'length');
The result will be the columns that exist.
There is no way to do what you want, except for constructing an SQL string from the list of available columns, which can be got by querying information_schema.columns.
SQL statements are parsed before they are executed, and there is no conditional compilation or no short-circuiting, so you get an error if a non-existing column is referenced.

Indexing on jsonb keys in postgresql

I'm using PostgreSQL.
Is there any way to create index just on dictionary keys, not values.
For example imagine a jsonb column like:
select data from tablename where id = 0;
answer: {1:'v1', 2:'v2'}
I want to index on the key set (or key list) which is [1, 2]. To speed up queries like:
select count(*) from tablename where data ? '2';
As you can see in docs, there is a way for indexing the column entirely (keys + values):
CREATE INDEX idxgin ON api USING GIN (jdoc);
This is not good for me, considering that I store a large amount of data in values.
I tried this before:
CREATE INDEX test ON tablename (jsonb_object_keys(data));
The error was:
ERROR: set-returning functions are not allowed in index expressions
Also, I don't want to store keys in the dictionary as a value.
Can you help me?
Your example doesn't make much sense, as your WHERE clause isn't specifying a JSON operation, and your example output is not valid JSON syntax.
You can hide the set-returning function (and the aggregate) into an IMMUTABLE function:
create function object_keys(jsonb) returns text[] language SQL immutable as $$
select array_agg(jsonb_object_keys) from jsonb_object_keys($1)
$$;
create index on tablename using gin ( object_keys(data));
If you did it this way, you could then query it formulated like this:
select * from tablename where object_keys(data) #> ARRAY['2'];
You could instead make the function return a JSONB containing an array rather than returning a PostgreSQL text array, if you would rather query it that way:
select * from tablename where object_keys_jsonb(data) #> '"2"';
You can't use a ? formulation, because in JSONB that is specifically for objects not arrays. If you really wanted to use ?, you could instead write a function which keeps the object as an object, but converts all the values to JSON null or to empty string, so they take up less space.

Custom column extension for slick

SQL expression generated by table.filter(_.id.inSetBind(someSet)) has restrictions for Oracle DB:
someSet must contains no more than 1000 elements (ORA-01795)
Oracle builds different excecution plans for queries with different size of someSet.
I want to create column extension, inArrayBind[T](s: Traversable[T]), that instead of creating query select ... where x IN (...), first creates Oracle type create or replace type MY_ARRAY IS varray(sizeOfSet) of type T and then perfrorms select ... where x in MY_ARRAY
Is there way to achieved this with slick?

Returning column value after insert into table and set it into a variable [duplicate]

I have a table. I wrote a function in plpgsql that inserts a row into this table:
INSERT INTO simpleTalbe (name,money) values('momo',1000) ;
This table has serial field called id. I want in the function after I insert the row to know the id that the new row received.
I thought to use:
select nextval('serial');
before the insert, is there a better solution?
Use the RETURNING clause. You need to save the result somewhere inside PL/pgSQL - with an appended INTO ..
INSERT INTO simpleTalbe (name,money) values('momo',1000)
RETURNING id
INTO _my_id_variable;
_my_id_variable must have been declared with a matching data type.
Related:
PostgreSQL next value of the sequences?
Depending on what you plan to do with it, there is often a better solution with pure SQL. Examples:
Combining INSERT statements in a data-modifying CTE with a CASE expression
PostgreSQL multi INSERT...RETURNING with multiple columns
select nextval('serial'); would not do what you want; nextval() actually increments the sequence, and then the INSERT would increment it again. (Also, 'serial' is not the name of the sequence your serial column uses.)
#Erwin's answer (INSERT ... RETURNING) is the best answer, as the syntax was introduced specifically for this situation, but you could also do a
SELECT currval('simpletalbe_id_seq') INTO ...
any time after your INSERT to retrieve the current value of the sequence. (Note the sequence name format tablename_columnname_seq for the automatically-defined sequence backing the serial column.)