How determine if range list contains specified integer - sql

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;

Related

integer[] type in Azure SQL Server

In PostgresSQL I used array types for storing ids, uuids etc like:
CREATE TABLE IF NOT EXISTS eventing.notifications (event_type integer NOT NULL, transport_type integer, user_id uuid, user_ids uuid[]);
or
CREATE TABLE IF NOT EXISTS public.sources (name character varying(255), timestamp without time zone, read_only_organization_ids integer[], organization_id uuid);
Is there an equivalent using Microsoft SQL server in Azure?
This is blurring the lines between SQL Server and nosql, however you can do it by encoding your array into a json array and store that in a varchar(max) column.
Then to create the json array from some other table storing user ids you would use the for json clause.
To get the original array out of the varchar column you can cross apply with the openjson function:
declare #notifications table (user_ids varchar(max))
declare #user_ids varchar(max)
;with cte_jsonUser(jsonIds) as
(
select id
from (values(1), (2)) as tbluser(id)
for json auto
)
insert into #notifications(user_ids)
select replace(replace(jsonIds,'{"id":',''),'}','')
from cte_jsonUser
select user_ids from #notifications
-- user_ids
-- [1,2]
select i.user_ids
from #notifications as n
cross apply openjson(n.user_ids)
with (user_ids int '$') as i
-- user_ids
-- 1
-- 2

MACRO to create a table in SQL

Hi everyone thanks so much for taking the time to read this.
I'd like to create a macro in Teradata that will create a table from another table based on specific parameters.
My original table consists of three columns patient_id, diagnosis_code and Date_of_birth
......
I'd like to build a macro that would allow me to specify a diagnosis code and it would then build the table consisting of data of all patients with that diagnosis code.
My current code looks like this
Create Macro All_pats (diag char) as (
create table pats as(
select *
from original_table
where diag = :diagnosis_code;)
with data primary index (patid);
I cant seem to get this to work - any tips?
Thanks once again
Your code has a semicolon in a wrong place and a missing closing bracket:
Create Macro All_pats (diag char) as (
create table pats as
(
select *
from original_table
where diag = :diagnosis_code
) with data primary index (patid);
);
Edit:
Passing multiple values as a delimited list is more complicated (unless you use Dynamic SQL in a Stored Procedure):
REPLACE MACRO All_lpats (diagnosis_codes VARCHAR( 1000)) AS
(
CREATE TABLE pats AS
(
SELECT *
FROM original_table AS t
JOIN TABLE (StrTok_Split_To_Table(1, :diagnosis_codes, ',')
RETURNS (outkey INTEGER,
tokennum INTEGER,
token VARCHAR(20) CHARACTER SET Unicode)
) AS dt
ON t.diag = dt.token
) WITH DATA PRIMARY INDEX (patid);
);
EXEC All_lpats('111,112,113');
As the name implies StrTok_Split_To_Table splits a delimited string into a table. You might need to adust the delimiter and the length of the resulting token.

Bulk update with associative array in PostgreSQL

Let's say I have an associative array (defined in another language) like so:
apply = {
'qwer': ['tju', 'snf', 'rjtj', 'sadgg']
'asdf': ['rtj', 'sfm', 'rtjt', 'adjdj']
...
'zxcv': ['qwr', 'trj', '3w4u', '3tt3']
}
And I have a table like so:
CREATE TABLE apples (
id integer,
name varchar(10),
key varchar(10),
value varchar(10)
);
I want to apply an update where if apples.value is in one of the lists of the apply variable, then set the apples.key to the key of the array. If apples.value was tju then set apples.key to qwer.
My current approach looks like this (mixing PostgreSQL with any procedural language):
for key in apply.keys:
UPDATE apples SET key=$key
FROM (SELECT unnest(array($apply[key])) AS value) AS update_table
WHERE value=update_table.value
I want to do this in a single statement.
As proof of concept for the given example, with the string formatted exactly as you display:
Demonstrating a prepared statement, like your client probably uses.
PREPARE my_update AS
UPDATE apples a
SET key = upd.key
FROM (
SELECT trim (split_part(key_val, ': ', 1), ' ''') AS key
, string_to_array(translate(split_part(key_val, ': ', 2), '[]''', ''), ', ') AS val_arr
FROM unnest(string_to_array(trim($1, E'{}\n'), E'\n')) key_val
) upd
WHERE a.value = ANY(upd.val_arr);
EXECUTE in the same session any number of times:
EXECUTE my_update($assoc_arr${
'qwer': ['tju', 'snf', 'rjtj', 'sadgg']
'asdf': ['rtj', 'sfm', 'rtjt', 'adjdj']
'zxcv': ['qwr', 'trj', '3w4u', '3tt3']
}$assoc_arr$);
SQL Fiddle.
Related:
Insert text with single quotes in PostgreSQL
Split given string and prepare case statement
But I would rather process the type in its original language and pass key and val_arr separately.

Split string into row

I am using MySQL.
I have a table named "class", the "class" table has three columns name , year and class_code like following:
Class table:
Now I would like to use the above table to create a new table named "temp", which contains class_code and value columns.
The rule is that each string value in each column field of a row of the above "class" table will be split into words, and each word will be inserted to the "temp" table as value column of a temp table record like following:
temp table:
I am using MySQL.
Is it possible to generate the "temp" table purely by using SQL statement and how?
That's :
CREATE TABLE temp;
ALTER TABLE temp DISABLE KEYS;
INSERT INTO ...(how to split the string value of each field in "class" table and insert to "temp" table??? )
P.S.:
I used a simple equal length string as a value, but the actually case has very random length string, and the number of words in each string is random also.
CREATE TABLE new_table
SELECT SUBSTRING(name, 1, 4) as field, class_code FROM old_table
UNION
SELECT SUBSTRING(name, 6, 4) as field, class_code FROM old_table
UNION
SELECT SUBSTRING(name, 11, 4) as field, class_code FROM old_table
UNION
SELECT SUBSTRING(year, 1, 4) as field, class_code FROM old_table
UNION
SELECT SUBSTRING(year, 6, 4) as field, class_code FROM old_table
You have to write a function for string splitting in MySql as
CREATE FUNCTION SPLIT_STR(
x VARCHAR(255),
delim VARCHAR(12),
pos INT
)
RETURNS VARCHAR(255)
RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1),
delim, '');
And use it to split the values and do the assigning the splitted values and inserting in to table in a SP.For more information refer to the MySQL Split String

Declare variable in SQLite and use it

I want to declare a variable in SQLite and use it in insert operation.
Like in MS SQL:
declare #name as varchar(10)
set name = 'name'
select * from table where name = #name
For example, I will need to get last_insert_row and use it in insert.
I have found something about binding but I didn't really fully understood it.
SQLite doesn't support native variable syntax, but you can achieve virtually the same using an in-memory temp table.
I've used the below approach for large projects and works like a charm.
/* Create in-memory temp table for variables */
BEGIN;
PRAGMA temp_store = 2; /* 2 means use in-memory */
CREATE TEMP TABLE _Variables(Name TEXT PRIMARY KEY, RealValue REAL, IntegerValue INTEGER, BlobValue BLOB, TextValue TEXT);
/* Declaring a variable */
INSERT INTO _Variables (Name) VALUES ('VariableName');
/* Assigning a variable (pick the right storage class) */
UPDATE _Variables SET IntegerValue = ... WHERE Name = 'VariableName';
/* Getting variable value (use within expression) */
... (SELECT coalesce(RealValue, IntegerValue, BlobValue, TextValue) FROM _Variables WHERE Name = 'VariableName' LIMIT 1) ...
DROP TABLE _Variables;
END;
For a read-only variable (that is, a constant value set once and used anywhere in the query), use a Common Table Expression (CTE).
WITH const AS (SELECT 'name' AS name, 10 AS more)
SELECT table.cost, (table.cost + const.more) AS newCost
FROM table, const
WHERE table.name = const.name
SQLite WITH clause
Herman's solution works, but it can be simplified because Sqlite allows to store any value type on any field.
Here is a simpler version that uses one Value field declared as TEXT to store any value:
CREATE TEMP TABLE IF NOT EXISTS Variables (Name TEXT PRIMARY KEY, Value TEXT);
INSERT OR REPLACE INTO Variables VALUES ('VarStr', 'Val1');
INSERT OR REPLACE INTO Variables VALUES ('VarInt', 123);
INSERT OR REPLACE INTO Variables VALUES ('VarBlob', x'12345678');
SELECT Value
FROM Variables
WHERE Name = 'VarStr'
UNION ALL
SELECT Value
FROM Variables
WHERE Name = 'VarInt'
UNION ALL
SELECT Value
FROM Variables
WHERE Name = 'VarBlob';
Herman's solution worked for me, but the ... had me mixed up for a bit. I'm including the demo I worked up based on his answer. The additional features in my answer include foreign key support, auto incrementing keys, and use of the last_insert_rowid() function to get the last auto generated key in a transaction.
My need for this information came up when I hit a transaction that required three foreign keys but I could only get the last one with last_insert_rowid().
PRAGMA foreign_keys = ON; -- sqlite foreign key support is off by default
PRAGMA temp_store = 2; -- store temp table in memory, not on disk
CREATE TABLE Foo(
Thing1 INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
);
CREATE TABLE Bar(
Thing2 INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
FOREIGN KEY(Thing2) REFERENCES Foo(Thing1)
);
BEGIN TRANSACTION;
CREATE TEMP TABLE _Variables(Key TEXT, Value INTEGER);
INSERT INTO Foo(Thing1)
VALUES(2);
INSERT INTO _Variables(Key, Value)
VALUES('FooThing', last_insert_rowid());
INSERT INTO Bar(Thing2)
VALUES((SELECT Value FROM _Variables WHERE Key = 'FooThing'));
DROP TABLE _Variables;
END TRANSACTION;
To use the one from denverCR in your example:
WITH tblCTE AS (SELECT "Joe" AS namevar)
SELECT * FROM table, tblCTE
WHERE name = namevar
As a beginner I found other answers too difficult to understand, hope this works
Creating "VARIABLE" for use in SQLite SELECT (and some other) statements
CREATE TEMP TABLE IF NOT EXISTS variable AS SELECT '2002' AS _year; --creating the "variable" named "_year" with value "2002"
UPDATE variable SET _year = '2021'; --changing the variable named "_year" assigning "new" value "2021"
SELECT _year FROM variable; --viewing the variable
SELECT 'TEST', (SELECT _year FROM variable) AS _year; --using the variable
SELECT taxyr FROM owndat WHERE taxyr = (SELECT _year FROM variable); --another example of using the variable
SELECT DISTINCT taxyr FROM owndat WHERE taxyr IN ('2022',(SELECT _year FROM variable)); --another example of using the variable
DROP TABLE IF EXISTS variable; --releasing the "variable" if needed to be released
After reading all the answers I prefer something like this:
select *
from table, (select 'name' as name) const
where table.name = const.name
Try using Binding Values. You cannot use variables as you do in T-SQL but you can use "parameters". I hope the following link is usefull.Binding Values
I found one solution for assign variables to COLUMN or TABLE:
conn = sqlite3.connect('database.db')
cursor=conn.cursor()
z="Cash_payers" # bring results from Table 1 , Column: Customers and COLUMN
# which are pays cash
sorgu_y= Customers #Column name
query1="SELECT * FROM Table_1 WHERE " +sorgu_y+ " LIKE ? "
print (query1)
query=(query1)
cursor.execute(query,(z,))
Don't forget input one space between the WHERE and double quotes
and between the double quotes and LIKE