Xml Extract in DB2 - db2

Below have the xml framing fields,
**Xml_value=
<docs><doc_id>1234</doc_id>
<reci><reci_code>ss</reci_code>
</reci></docs>**
Select doc.value('doc_id[1]',varchar(8)')doc_id,
Reci.value('reci_code'[1],char(8)')reci_code
From xml_value.nodes('docs/doc')docs(doc)
cross apply docs.nodes('reci/reci')reci'(reci')
Output:
doc_id reci_code
1234 ss
The above mentioned query is for SQL server.
need to extract all XML fields into a separate columns as like above output statement in db2.
How to achieve this in db 2.
can you help on this.

Try this:
/*
WITH MYTABLE AS
(
SELECT T.*
FROM
(
VALUES XMLPARSE
(
DOCUMENT '
<docs>
<doc_id>1234</doc_id>
<reci>
<reci_code>ss</reci_code>
</reci>
</docs>
'
)
) T(X)
)
*/
SELECT V.DOC_ID, V.RECI_CODE
FROM
MYTABLE T
, XMLTABLE
(
'$D/docs/reci' PASSING T.X AS "D"
COLUMNS
DOC_ID INT PATH '../doc_id'
, RECI_CODE VARCHAR(10) PATH 'reci_code'
) V;
|DOC_ID |RECI_CODE |
|----------|----------|
|1234 |ss |

Related

"Subquery returns more than 1 value" when trying to INSERT

I'm trying to INSERT into a table a column that is part of another column in another table using TSQL, but I get the error stating that there is more than one value returned when I used that subquery as an expression. I understand what causes the error, but I can't seem to think of a way to make it produce what I want.
I'm trying to do something similar to:
A.Base B.Reference C.Wanted
--- ---- ----
abcdaa aa abcdaa
bcdeab bb cdefbb
cdefbb cc efghcc
defgbc ddd fghddd
efghcc
fghddd
So I'm using the code:
INSERT INTO C ( [Some other column], Wanted )
SELECT
A.[Some other column],
, CASE
WHEN LEN( B.Reference ) = 2 THEN
( SELECT A.Base FROM A WHERE RIGHT( A.Base, 2 ) =
( SELECT B.Reference FROM B WHERE LEN( B.Reference ) = 2 )
)
WHEN LEN( B.Reference ) = 3 THEN
( SELECT A.Base FROM A WHERE RIGHT( A.Base, 3 ) =
( SELECT B.Reference FROM B WHERE LEN( B.Reference ) = 3 )
)
END
FROM
A
, B
Which will return me the "more than 1 value" error. Honestly, I'm probably making this way more convoluted than it needs to be, but I've been staring at these tables for a while now.
I hope I'm getting the idea across as to what I'm trying to do.
If you know the records aren't duplicate, and you are sure your JOIN between A and B works (as Martin mentioned) can't you just select distinct to return just the unique records?
I'd try it like this:
--Create a mockup with declared table variables and test data
DECLARE #tblA TABLE(someColumnInA VARCHAR(100));
DECLARE #tblB TABLE(someColumnInB VARCHAR(100));
DECLARE #tblC TABLE(someColumnInC VARCHAR(100));
INSERT INTO #tblA VALUES
('abcdaa')
,('bcdeab')
,('cdefbb')
,('defgbc')
,('efghcc')
,('fghddd')
INSERT INTO #tblB VALUES
('aa')
,('bb')
,('cc')
,('ddd');
--The query
INSERT INTO #tblC(someColumnInC)
SELECT SomeColumnInA
FROM #tblA a
WHERE EXISTS(SELECT 1 FROM #tblB b WHERE a.someColumnInA LIKE '%' + b.SomeColumnInB + '%');
SELECT * FROM #tblC;
The idea in short:
After creating a mockup (please do this next time in advance) we use a query to insert all values from #tblA into #tblC as long as there exists any value in #tblB, which is part of the current value in #tblA.
How about doing something like this?
select *
from A
where RIGHT(A.Base,2) IN (select B.Reference FROM B WHERE LEN(B.Reference) = 2)
UNION ALL
select *
from A
where RIGHT(A.Base,3) IN (select B.Reference FROM B WHERE LEN(B.Reference) = 3)

How do I run nested query with function that returns recordset?

How do I run nested query with function that returns recordset ?
What I'd like to do is to parse TEXT field and split it to stems.
It should something like this
with strings as (
select text
from messages
where channel_id = 12345 and text like '%#%'
)
select distinct token
from ts_debug('russian', strings.text)
where alias = 'word';
But obviously it doesn't work because ts_debug returns recordset.
How can I do this ?
Use a lateral join:
with strings as (
select text
from messages
where channel_id = 12345 and text like '%#%'
)
select distinct td.token
from strings
cross join lateral ts_debug('russian', strings.text) as td
where alias = 'word';
maу be replaсe strings.text to (SELECT text FROM strings)
with strings as (
select text
from messages
where channel_id = 12345 and text like '%#%'
)
select distinct token
from ts_debug('russian', (SELECT text FROM strings))
where alias = 'word';
OR to JOIN
with strings as (
select text
from messages
where channel_id = 12345 and text like '%#%'
)
select distinct token
from strings
INNER JOIN ts_debug('russian', strings.text) AS tsd ON true
where alias = 'word';

PostgreSQL Substring extraction

I have a column called 'uri' which has values as follows:
file://c:\file1.txt
file://\\1.1.1.1\folder1\folder1a\file2.txt
file://d:\sub1\sub2\sub3\file3.txt
I'm trying to remove the filename from the uri to leave
file://c:\
file://\\1.1.1.1\folder1\folder1a\
file://d:\sub1\sub2\sub3\
You can use regexp_replace:
with t (col) as (
select 'file://c:\file1.txt' union all
select 'file://\\1.1.1.1\folder1\folder1a\file2.txt' union all
select 'file://d:\sub1\sub2\sub3\file3.txt'
)
select regexp_replace(col, '(^.*?)[^\\]*$', '\1') as output from t;
Produces:
output
---------------------------------
file://c:\
file://\\1.1.1.1\folder1\folder1a\
file://d:\sub1\sub2\sub3\
Demo

How to count the frequency of integers in a set of querystrings in postgres

I have a column in a postgres database which logs search querystrings for a page on our website.
The column contains data like
"a=2&b=4"
"a=2,3"
"b=4&a=3"
"a=4&a=3"
I'd like to work out the frequency of each value for a certain parameter (a).
value | freq
------|------
3 | 3
2 | 2
4 | 1
Anyway to do this in a single SQL statement?
Something like this:
with all_values as (
select string_to_array(split_part(parameter, '=', 2), ',') as query_params
from the_table d,
unnest(string_to_array(d.querystring, '&')) as x(parameter)
where x.parameter like 'a%'
)
select t.value, count(*)
from all_values av, unnest(av.query_params) as t(value)
group by t.value
order by t.value;
Online example: http://rextester.com/OXM67442
try something like this :
select data_value,count(*) from (
select data_name,unnest(string_to_array(data_values,',')) data_value from (
select split_part(data_array,'=',1) data_name ,split_part(data_array,'=',2) data_values from (
select unnest(string_to_array(mydata,'&')) data_array from mytable
) a
) b
) c where data_name='a' group by 1 order by 1
Assuming tha table that keeps the counts is called paramcount:
WITH vals(v) AS
(SELECT regexp_replace(p, '^.*=', '')
FROM regexp_split_to_table(
'b=4&a=3,2',
'&|,'
) p(p)
)
INSERT INTO paramcount (value, freq)
SELECT v, 1 FROM vals
ON CONFLICT (value)
DO UPDATE SET freq = paramcount.freq + 1
WHERE paramcount.value = EXCLUDED.value;
get csv integer after 'a='
split that to numbers
stat values
select v, count(*) from (
SELECT c,unnest(string_to_array(unnest(regexp_matches(c,'a=([0-9,]+)','g')),',')) as v FROM qrs
) x group by v;
Parametrize:
WITH argname(aname) as (values ('a'::TEXT))
select v, count(*) from (SELECT c,unnest(string_to_array(unnest(regexp_matches(c,aname||'=([0-9,]+)','g')),',')) as v FROM qrs,argname) x group by v;

How can I SUM distinct records in a Postgres database where there are duplicate records?

Imagine a table that looks like this:
The SQL to get this data was just SELECT *
The first column is "row_id" the second is "id" - which is the order ID and the third is "total" - which is the revenue.
I'm not sure why there are duplicate rows in the database, but when I do a SUM(total), it's including the second entry in the database, even though the order ID is the same, which is causing my numbers to be larger than if I select distinct(id), total - export to excel and then sum the values manually.
So my question is - how can I SUM on just the distinct order IDs so that I get the same revenue as if I exported to excel every distinct order ID row?
Thanks in advance!
Easy - just divide by the count:
select id, sum(total) / count(id)
from orders
group by id
See live demo.
Also handles any level of duplication, eg triplicates etc.
You can try something like this (with your example):
Table
create table test (
row_id int,
id int,
total decimal(15,2)
);
insert into test values
(6395, 1509, 112), (22986, 1509, 112),
(1393, 3284, 40.37), (24360, 3284, 40.37);
Query
with distinct_records as (
select distinct id, total from test
)
select a.id, b.actual_total, array_agg(a.row_id) as row_ids
from test a
inner join (select id, sum(total) as actual_total from distinct_records group by id) b
on a.id = b.id
group by a.id, b.actual_total
Result
| id | actual_total | row_ids |
|------|--------------|------------|
| 1509 | 112 | 6395,22986 |
| 3284 | 40.37 | 1393,24360 |
Explanation
We do not know what the reasons is for orders and totals to appear more than one time with different row_id. So using a common table expression (CTE) using the with ... phrase, we get the distinct id and total.
Under the CTE, we use this distinct data to do totaling. We join ID in the original table with the aggregation over distinct values. Then we comma-separate row_ids so that the information looks cleaner.
SQLFiddle example
http://sqlfiddle.com/#!15/72639/3
Create custom aggregate:
CREATE OR REPLACE FUNCTION sum_func (
double precision, pg_catalog.anyelement, double precision
)
RETURNS double precision AS
$body$
SELECT case when $3 is not null then COALESCE($1, 0) + $3 else $1 end
$body$
LANGUAGE 'sql';
CREATE AGGREGATE dist_sum (
pg_catalog."any",
double precision)
(
SFUNC = sum_func,
STYPE = float8
);
And then calc distinct sum like:
select dist_sum(distinct id, total)
from orders
SQLFiddle
You can use DISTINCT in your aggregate functions:
SELECT id, SUM(DISTINCT total) FROM orders GROUP BY id
Documentation here: https://www.postgresql.org/docs/9.6/static/sql-expressions.html#SYNTAX-AGGREGATES
If we can trust that the total for 1 order is actually 1 row. We could eliminate the duplicates in a sub-query by selecting the the MAX of the PK id column. An example:
CREATE TABLE test2 (id int, order_id int, total int);
insert into test2 values (1,1,50);
insert into test2 values (2,1,50);
insert into test2 values (5,1,50);
insert into test2 values (3,2,100);
insert into test2 values (4,2,100);
select order_id, sum(total)
from test2 t
join (
select max(id) as id
from test2
group by order_id) as sq
on t.id = sq.id
group by order_id
sql fiddle
In difficult cases:
select
id,
(
SELECT SUM(value::int4)
FROM jsonb_each_text(jsonb_object_agg(row_id, total))
) as total
from orders
group by id
I would suggest just use a sub-Query:
SELECT "a"."id", SUM("a"."total")
FROM (SELECT DISTINCT ON ("id") * FROM "Database"."Schema"."Table") AS "a"
GROUP BY "a"."id"
The Above will give you the total of each id
Use below if you want the full total of each duplicate removed:
SELECT SUM("a"."total")
FROM (SELECT DISTINCT ON ("id") * FROM "Database"."Schema"."Table") AS "a"
Using subselect (http://sqlfiddle.com/#!7/cef1c/51):
select sum(total) from (
select distinct id, total
from orders
)
Using CTE (http://sqlfiddle.com/#!7/cef1c/53):
with distinct_records as (
select distinct id, total from orders
)
select sum(total) from distinct_records;