DB2 XML Select multiple rows in Single statements - db2

In My code,
SELECT X.DEP_ID
FROM (SELECT XMLPARSE (DOCUMENT '<root><DEP_ID>1000000004</DEP_ID><DEP_ID>1000000005</DEP_ID></root>') AS ELEMENT_VALUE
FROM SYSIBM.SYSDUMMY1) AS A,
XMLTABLE (
'$d/root'
PASSING Element_value AS "d"
COLUMNS
DEP_ID VARCHAR (10) PATH 'DEP_ID'
) AS X;
Need as result of:
DEP_ID
1000000004
1000000005
If its single values means it working that means only one DEP_ID in xml.
But Multiple return means it will show error.
How to get the output as like above in db2.

Wrong row-xquery-expression-constant.
Try this:
SELECT X.DEP_ID
FROM
(
SELECT XMLPARSE (DOCUMENT '<root><DEP_ID>1000000004</DEP_ID><DEP_ID>1000000005</DEP_ID></root>') AS ELEMENT_VALUE
FROM SYSIBM.SYSDUMMY1
) AS A
, XMLTABLE
(
'$d/root/DEP_ID' PASSING Element_value AS "d"
COLUMNS
DEP_ID VARCHAR (10) PATH '.'
) AS X;

Related

Postgres Crosstab query with CTE (with clause)

Recently started working on Postgres and need to pivot data.
I wrote the following query:
select *
from crosstab (
$$
with tmp_kv as (
select distinct pat_id
,col.name as key, replace(replace(replace(value, '[',''), ']', ''),'"','') as value
from (
select p.Id as pat_id, nullif(kv.key,'undefined')::int as key, trim(kv.value::text,'"') as value
from pat_table p
left join e_table e on e.pat_id = p.id and e.id is null
,jsonb_each_text(p.data) as kv
) t
left join lateral (
select name::text as name from public.config_fields fld
where id = t.key
) col on true
)
select pat_id, key, value
from tmp_kv
where nullif(trim(key),'') is not null
order by pat_id, key
$$,$$
select distinct key from tmp_kv -- (Get error "relation "tmp_kv" does not exist" )
where nullif(trim(key),'') is not null
order by 1
$$
) as (
pat_id bigint
...
...
);
Query works if I take the WITH clause out into temporary table. But will be deploying it to production with read replicas, so need it to be working with a CTE. Is there a way?
The two queries passed as strings to the crosstab() function are separate queries.
A CTE can only be attached to a single query.
What you ask for is strictly impossible.
Since you have to spell out the (static) return type for crosstab() anyway, and the result of the query in the 2nd parameter has to match that, it's pointless to use a query with a dynamic result as 2nd parameter to begin with.

DB2 - String operations

Can we check a string value with comma separated exists in another comma separated string in DB2?
I have say two string like below
Case-1:
String-1:'ABC,XYZ,PQR'
Srting-2:'MNO,PQR'
I want to have any function in DB2 which should return 1 in the above case as PQR from String-2 exists in String-1. The value should match any string within comma or after or before comma.
Case-2:
String-1:'ABC,XYZ,PQR'
Srting-2:'MNO,P QR'
But for the above case the same function should return 0. As there is space after "P"
I also want to have a similar function which should return the concatenated string but removes duplicates.
Case-1:
String-1:'ABC,XYZ,PQR'
Srting-2:'MNO,PQR'
Output should be 'ABC,XYZ,PQR,MNO' .
The order in comma separated values doesn't matter in this case; but PQR should not be repeated twice.
Case-2:
String-1:'ABC,XYZ,PQR'
Srting-2:'MNO,P QR'
In this case output should be 'ABC,XYZ,PQR,MNO,P QR' .
I am trying to achieve this using SQL or any function in DB2.
There is another case where I want to remove matching part of the string
Case-3:
String-1:'ABC,XYZ,PQR'
Srting-2:'MNO,PQR'
Output should be 'ABC,XYZ,MNO'
Obviously String-1 and String-2 can have any number of values separated by comma.
Try this:
CREATE FUNCTION SPLITTER (P_STR VARCHAR(4000), P_DELIM VARCHAR(10))
RETURNS TABLE (SEQ INT, TOKEN VARCHAR(50))
RETURN
SELECT TOK.SEQ, TOK.TOKEN
FROM XMLTABLE
(
'for $id in tokenize($s, $t) return <i>{string($id)}</i>' PASSING P_STR AS "s", P_DELIM AS "t"
COLUMNS
SEQ FOR ORDINALITY
, TOKEN VARCHAR(50) PATH '.'
) TOK;
SELECT
CASE
WHEN EXISTS
(
SELECT 1
FROM TABLE (SPLITTER ('ABC,XYZ,PQR', ',')) A
JOIN TABLE (SPLITTER ('MNO,PQR', ',')) B ON B.TOKEN = A.TOKEN
)
THEN 1
ELSE 0
END
FROM SYSIBM.SYSDUMMY1;
SELECT LISTAGG (TOKEN, ',')
FROM
(
SELECT TOKEN
FROM TABLE (SPLITTER ('ABC,XYZ,PQR', ','))
UNION
SELECT TOKEN
FROM TABLE (SPLITTER ('MNO,PQR', ','))
);
SELECT LISTAGG (TOKEN, ',')
FROM
(
SELECT DISTINCT COALESCE (A.TOKEN, B.TOKEN) AS TOKEN
FROM TABLE (SPLITTER ('ABC,XYZ,PQR', ',')) A
FULL JOIN TABLE (SPLITTER ('MNO,PQR', ',')) B ON B.TOKEN = A.TOKEN
WHERE A.TOKEN IS NULL OR B.TOKEN IS NULL
);
dbfiddle link.

Removing all the Alphabets from a string using a single SQL Query [duplicate]

I'm currently doing a data conversion project and need to strip all alphabetical characters from a string. Unfortunately I can't create or use a function as we don't own the source machine making the methods I've found from searching for previous posts unusable.
What would be the best way to do this in a select statement? Speed isn't too much of an issue as this will only be running over 30,000 records or so and is a once off statement.
You can do this in a single statement. You're not really creating a statement with 200+ REPLACEs are you?!
update tbl
set S = U.clean
from tbl
cross apply
(
select Substring(tbl.S,v.number,1)
-- this table will cater for strings up to length 2047
from master..spt_values v
where v.type='P' and v.number between 1 and len(tbl.S)
and Substring(tbl.S,v.number,1) like '[0-9]'
order by v.number
for xml path ('')
) U(clean)
Working SQL Fiddle showing this query with sample data
Replicated below for posterity:
create table tbl (ID int identity, S varchar(500))
insert tbl select 'asdlfj;390312hr9fasd9uhf012 3or h239ur ' + char(13) + 'asdfasf'
insert tbl select '123'
insert tbl select ''
insert tbl select null
insert tbl select '123 a 124'
Results
ID S
1 390312990123239
2 123
3 (null)
4 (null)
5 123124
CTE comes for HELP here.
;WITH CTE AS
(
SELECT
[ProductNumber] AS OrigProductNumber
,CAST([ProductNumber] AS VARCHAR(100)) AS [ProductNumber]
FROM [AdventureWorks].[Production].[Product]
UNION ALL
SELECT OrigProductNumber
,CAST(STUFF([ProductNumber], PATINDEX('%[^0-9]%', [ProductNumber]), 1, '') AS VARCHAR(100) ) AS [ProductNumber]
FROM CTE WHERE PATINDEX('%[^0-9]%', [ProductNumber]) > 0
)
SELECT * FROM CTE
WHERE PATINDEX('%[^0-9]%', [ProductNumber]) = 0
OPTION (MAXRECURSION 0)
output:
OrigProductNumber ProductNumber
WB-H098 098
VE-C304-S 304
VE-C304-M 304
VE-C304-L 304
TT-T092 092
RichardTheKiwi's script in a function for use in selects without cross apply,
also added dot because in my case I use it for double and money values within a varchar field
CREATE FUNCTION dbo.ReplaceNonNumericChars (#string VARCHAR(5000))
RETURNS VARCHAR(1000)
AS
BEGIN
SET #string = REPLACE(#string, ',', '.')
SET #string = (SELECT SUBSTRING(#string, v.number, 1)
FROM master..spt_values v
WHERE v.type = 'P'
AND v.number BETWEEN 1 AND LEN(#string)
AND (SUBSTRING(#string, v.number, 1) LIKE '[0-9]'
OR SUBSTRING(#string, v.number, 1) LIKE '[.]')
ORDER BY v.number
FOR
XML PATH('')
)
RETURN #string
END
GO
Thanks RichardTheKiwi +1
Well if you really can't use a function, I suppose you could do something like this:
SELECT REPLACE(REPLACE(REPLACE(LOWER(col),'a',''),'b',''),'c','')
FROM dbo.table...
Obviously it would be a lot uglier than that, since I only handled the first three letters, but it should give the idea.

Use result of postgres CTE in function

I am having difficulty using the results from a CTE in a function. Given the following Postgres table.
CREATE TABLE directory (
id SERIAL PRIMARY KEY
, name TEXT
, parent_id INTEGER REFERENCES directory(id)
);
INSERT INTO directory (name, parent_id)
VALUES ('Root', NULL), ('D1', 1), ('D2', 2), ('D3', 3);
I have this recursive CTE that returns the descendants of a directory.
WITH RECURSIVE tree AS (
SELECT id
FROM directory
WHERE parent_id = 2
UNION ALL
SELECT directory.id
FROM directory, tree
WHERE directory.parent_id = tree.id
)
The returned values are what I expect and can be made to equal an array
SELECT (SELECT array_agg(id) FROM tree) = ARRAY[3, 4];
I can use an array to select values from the table
SELECT * FROM directory WHERE id = ANY(ARRAY[3, 4]);
However, I cannot use the results of the CTE to accomplish the same thing.
SELECT * FROM directory WHERE id = ANY(SELECT array_agg(id) FROM tree);
The resulting error indicates that there is a type mismatch.
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
However, I am unsure how to correctly accomplish this.
Use:
SELECT *
FROM directory
WHERE id = ANY(SELECT unnest(array_agg(id)) FROM tree);
See detailed explanation in this answer.
Using unnest() in a subquery is a general method for dealing with arrays:
where id = any(select unnest(some_array))
Because array_agg() and unnest() are inverse operations, the query can be as simply as:
SELECT *
FROM directory
WHERE id = ANY(SELECT id FROM tree);

tsql - using internal stored procedure as parameter is where clause

I'm trying to build a stored procedure that makes use of another stored procedure. Taking its result and using it as part of its where clause, from some reason I receive an error:
Invalid object name 'dbo.GetSuitableCategories'.
Here is a copy of the code:
select distinct top 6 * from
(
SELECT TOP 100 *
FROM [dbo].[products] products
where products.categoryId in
(select top 10 categories.categoryid from
[dbo].[GetSuitableCategories]
(
-- #Age
-- ,#Sex
-- ,#Event
1,
1,
1
) categories
ORDER BY NEWID()
)
--and products.Price <=#priceRange
ORDER BY NEWID()
)as d
union
select * from
(
select TOP 1 * FROM [dbo].[products] competingproducts
where competingproducts.categoryId =-2
--and competingproducts.Price <=#priceRange
ORDER BY NEWID()
) as d
and here is [dbo].[GetSuitableCategories] :
if (#gender =0)
begin
select * from categoryTable categories
where categories.gender =3
end
else
begin
select * from categoryTable categories
where categories.gender = #gender
or categories.gender =3
end
I would use an inline table valued user defined function. Or simply code it inline is no re-use is required
CREATE dbo.GetSuitableCategories
(
--parameters
)
RETURNS TABLE
AS
RETURN (
select * from categoryTable categories
where categories.gender IN (3, #gender)
)
Some points though:
I assume categoryTable has no gender = 0
Do you have 3 genders in your categoryTable? :-)
Why do pass in 3 parameters but only use 1? See below please
Does #sex map to #gender?
If you have extra processing on the 3 parameters, then you'll need a multi statement table valued functions but beware these can be slow
You can't use the results of a stored procedure directly in a select statement
You'll either have to output the results into a temp table, or make the sproc into a table valued function to do what you doing.
I think this is valid, but I'm doing this from memory
create table #tmp (blah, blah)
Insert into #tmp
exec dbo.sprocName