SQL test if ARRAY contains integer - sql

I have a SQL TABLE like
CREATE TABLE Test ( ContentArray INTEGER ARRAY )`.
I can insert an column with `INSERT INTO Test ( ContentArray ) VALUES ( ARRAY[1,2,3,4] )
But I don't know how to get all columns where ContentArray contains integer 3.
For example:
INSERT INTO Test ( ContentArray ) VALUES ( ARRAY[1,2] )
INSERT INTO Test ( ContentArray ) VALUES ( ARRAY[3,4] )
INSERT INTO Test ( ContentArray ) VALUES ( ARRAY[4,5] )
INSERT INTO Test ( ContentArray ) VALUES ( ARRAY[5,6] )
SELECT ContentArray FROM Test WHERE /* ContentArray contains 5 and 6 */
should return ARRAY[5,6].

Given the DBMS and some quick research, it looks like SQL2008 supports the ANY keyword to allow this type of query for array datatypes:
SELECT * FROM Test WHERE 3 = ANY (ContentArray);
I still wouldn't recommend using the array data type for anything non-trivial, I'm not sure what the performance of the above query would be like, but I'm sure it's not as good as a master table with children.

Related

How determine if range list contains specified integer

Product type table contains product types. Some ids may missing :
create table artliik (liiginrlki char(3) primary key);
insert into artliik values('1');
insert into artliik values('3');
insert into artliik values('4');
...
insert into artliik values('999');
Property table contais comma separated list of types.
create table strings ( id char(100) primary key, kirjeldLku chr(200) );
insert into strings values ('item1', '1,4-5' );
insert into strings values ('item2', '1,2,3,6-9,23-44,45' );
Type can specified as single integer, e.q 1,2,3 or as range like 6-9 or 23-44
List can contain both of them.
How to all properties for given type.
Query
select id
from artliik
join strings on ','||trim(strings.kirjeldLku)||',' like '%,'||trim(artliik.liiginrlki)||',%'
returns date for single integer list only.
How to change join so that type ranges in list like 6-9 are also returned?
Eq. f list contains 6-9, Type 6,7,8 and 9 shoud included in report.
Postgres 13 is used.
I would suggest a helper function similar to unnest that honors ranges.
Corrected function
create or replace function unnest_ranges(s text)
returns setof text language sql immutable as
$$
with t(x) as (select unnest(string_to_array(s, ',')))
select generate_series
(
split_part(x, '-', 1)::int,
case when x ~ '-' then split_part(x, '-', 2)::int else x::int end,
1
)::text
from t;
$$;
Then you can 'normalize' table strings and join.
select *
from artliik a
join (select id, unnest_ranges(kirjeldLku) from strings) as t(id, v)
on a.liiginrlki = v;
The use of a function definition is of course optional. I prefer it because the function is generic and reusable.
dbfiddle.uk demo will only works on pg14, since only pg14 have multirange data type. But customizeable icu collation works in pg13.
Collation doc: https://www.postgresql.org/docs/current/collation.html
Idea: create a multirange text data type that will sort numeric value based on their numerical value. like 'A-21' < 'A-123'.
CREATE COLLATION testcoll_numeric (
provider = icu,
locale = '#colNumeric=yes'
);
CREATE TYPE textrange AS RANGE (
subtype = text,
multirange_type_name = mulitrange_of_text,
COLLATION = testcoll_numeric
);
So
SELECT
mulitrange_of_text (textrange ('1'::text, '11'::text)) #> '9'::text AS contain_9;
should return true.
artliik table structure remain the same, but strings table need to change a bit.
CREATE temp TABLE strings (
id text PRIMARY KEY,
kirjeldLku mulitrange_of_text
);
then query it:
SELECT DISTINCT
strings.id
FROM
artliik,
strings
WHERE
strings.kirjeldLku #> liiginrlki::text
ORDER BY
1;

How to write a WHERE clause for NULL value in ARRAY type column?

I created a table which contains a column of string ARRAY type as:
CREATE TABLE test
(
id integer NOT NULL,
list text[] COLLATE pg_catalog."default",
CONSTRAINT test_pkey PRIMARY KEY (id)
)
I then added rows which contain various values for that array, including an empty array and missing data (null):
insert into test (id, list) values (1, array['one', 'two', 'three']);
insert into test (id, list) values (2, array['four']);
insert into test (id, list) values (3, array['']);
insert into test (id, list) values (4, array[]::text[]); // empty array
insert into test (id, list) values (5, null); // missing value
pgAdmin shows table like this:
I am trying to get a row which contains a null value ([null]) in the list column but:
select * from test where list = null;
...returns no rows and:
select * from test where list = '{}';
...returns row with id = 4.
How to write WHERE clause which would target NULL value for column of ARRAY type?
demo:db<>fiddle
... WHERE list IS NULL
select * from test where list IS null;
Like this:
select * from test where list IS NULL;

Hive - How to insert in a hive table an array of struct

So I learnt from here how to insert values into an array column:
INSERT INTO table
SELECT ARRAY("line1", "line2", "line3") as myArray
FROM source1;
And from here how to insert values into an struct column:
INSERT INTO table
SELECT NAMED_STRUCT('houseno','123','streetname','GoldStreet', 'town','London', 'postcode','W1a9JF') AS address
FROM source2;
Now I was trying to insert in the same way values in an array of structs. Which has got the following schema:
additionalattribute:array<struct<attribute_value:string,key:string,value:string>
I tried to extrapolate like this:
INSERT INTO table
ARRAY(NAMED_STRUCT('attribute_value','null','key','null','value','null')) as additionalattribute
FROM source2;
But it is not working. Does anyone know how to approach this issue?
you are missing the select statement after the table name. Demo
create table temp4
(
additionalattribute array<struct<attribute_value:string,key:string,value:string>>
);
INSERT INTO temp4 select
ARRAY(NAMED_STRUCT('attribute_value','null','key','null','value','null')) as additionalattribute
FROM (select '1' ) t;

How to manipulate comma-separated list in SQL Server

I have a list of values such as
1,2,3,4...
that will be passed into my SQL query.
I need to have these values stored in a table variable. So essentially I need something like this:
declare #t (num int)
insert into #t values (1),(2),(3),(4)...
Is it possible to do that formatting in SQL Server? (turning 1,2,3,4... into (1),(2),(3),(4)...
Note: I can not change what those values look like before they get to my SQL script; I'm stuck with that list. also it may not always be 4 values; it could 1 or more.
Edit to show what values look like: under normal circumstances, this is how it would work:
select t.pk
from a_table t
where t.pk in (#place_holder#)
#placeholder# is just a literal place holder. when some one would run the report, #placeholder# is replaced with the literal values from the filter of that report:
select t.pk
from a_table t
where t.pk in (1,2,3,4) -- or whatever the user selects
t.pk is an int
note: doing
declare #t as table (
num int
)
insert into #t values (#Placeholder#)
does not work.
Your description is a bit ridicuolus, but you might give this a try:
Whatever you mean with this
I see what your trying to say; but if I type out '#placeholder#' in the script, I'll end up with '1','2','3','4' and not '1,2,3,4'
I assume this is a string with numbers, each number between single qoutes, separated with a comma:
DECLARE #passedIn VARCHAR(100)='''1'',''2'',''3'',''4'',''5'',''6'',''7''';
SELECT #passedIn; -->: '1','2','3','4','5','6','7'
Now the variable #passedIn holds exactly what you are talking about
I'll use a dynamic SQL-Statement to insert this in a temp-table (declared table variable would not work here...)
CREATE TABLE #tmpTable(ID INT);
DECLARE #cmd VARCHAR(MAX)=
'INSERT INTO #tmpTable(ID) VALUES (' + REPLACE(SUBSTRING(#passedIn,2,LEN(#passedIn)-2),''',''','),(') + ');';
EXEC (#cmd);
SELECT * FROM #tmpTable;
GO
DROP TABLE #tmpTable;
UPDATE 1: no dynamic SQL necessary, all ad-hoc...
You can get the list of numbers as derived table in a CTE easily.
This can be used in a following statement like WHERE SomeID IN(SELECT ID FROM MyIDs) (similar to this: dynamic IN section )
WITH MyIDs(ID) AS
(
SELECT A.B.value('.','int') AS ID
FROM
(
SELECT CAST('<x>' + REPLACE(SUBSTRING(#passedIn,2,LEN(#passedIn)-2),''',''','</x><x>') + '</x>' AS XML) AS AsXml
) as tbl
CROSS APPLY tbl.AsXml.nodes('/x') AS A(B)
)
SELECT * FROM MyIDs
UPDATE 2:
And to answer your question exactly:
With this following the CTE
insert into #t(num)
SELECT ID FROM MyIDs
... you would actually get your declared table variable filled - if you need it later...

postgresql: select returning ARRAY

I have a table as:
CREATE TABLE tbl_temp (id serial, friend_id int, name varchar(32));
I wish I could run the following SQL:
PREPARE x AS SELECT {$1,friend_id} FROM tbl_temp WHERE id = ANY($2);
EXECUTE x(33, ARRAY[1,2,3,4])
I basically looking for a statement that will return me an array of two ints first of which will be user input and second will be from table column like friend_id.
Is it really possible in PostgreSQL?
The results from SELECT ($1, friend_id) FROM tbl_temp;
EXECUTE x(44);
row
--------
(44,1)
(44,2)
(44,3)
(3 rows)
If I use PQgetvalue(PGres, 0, 0) how will the result look like: {44,45} or like (44,45)?
I think you want to use the array constructor syntax:
SELECT ARRAY[$1, friend_id] FROM tbl_temp WHERE id = ANY($2)
i'm not sure i understand what you want...
to return an array, do this.
SELECT (44, "friend_id") FROM "tbl_temp" WHERE id = ANY(ARRAY[1,2,3,4]);