MariaDB's `JSON_LENGTH` - alternative for PostgreSQL - sql

I need to convert some queries written for MariaDB to PostgreSQL syntax. Unfortunately they use the JSON_LENGTH function of MariaDB and im struggling to find an alternative with PostgreSQL.
For clarification:
JSON_LENGTH counts the number of 'entries' on the root level of a JSON object/array in MariaDB:
SELECT
JSON_LENGTH('{"test": 123}') as test1, -- 1
JSON_LENGTH('"123"') as test2, -- 1
JSON_LENGTH('123') as test3, -- 1
JSON_LENGTH('[]') as test4, -- 0
JSON_LENGTH('[123]') as test5, -- 1
JSON_LENGTH('[123, 456]') as test6, -- 2
JSON_LENGTH('[123, {"test": 123}]') as test7; -- 2
Partial solutions in PostgreSQL I came up with:
select
json_array_length('[]'::json) as test1, -- 0
json_array_length('["a"]'::json) as test2, -- 1
length(json_object_keys('{"a": "b"}'::json)) as test3; -- 1
json_array_length is not allowed for JSON objects
json_object_keys is not allowed for JSON arrays
Unfortunately I can't manage to combine the two methods I figured out for PostgreSQL:
I tried to use CASE WHEN:
select
case
when json_typeof('["a"]'::json) = 'array' then json_array_length('["a"]'::json)
when json_typeof('["a"]'::json) = 'object' then length(json_object_keys('["a"]'::json))
else 0
end;
Error:
[0A000] ERROR: set-returning functions are not allowed in CASE
-> json_object_keys is the bad guy here
I tried IF ELSE:
select
IF ('array' = json_typeof('["a"]'::json)) THEN json_array_length('["a"]'::json)
ELEIF (json_typeof('["a"]'::json) = 'object') THEN length(json_object_keys('["a"]'::json))
ELSE 0;
Error:
[42601] ERROR: syntax error at or near "THEN"
I can imagine I have an error in my IF ELSE statement, but I'm unable to figure it out.
Is there any way to replicate MariaDB's JSON_LENGTH behavior with PostgreSQL?

You need to count the number of rows returned by jsonb_object_keys():
This:
with data (input) as (
values
('{"test": 123}'::jsonb),
('"123"'),
('123'),
('[]'),
('[123]'),
('[123, 456]'),
('[123, {"test": 123}]')
)
select input,
case jsonb_typeof(input)
when 'array' then jsonb_array_length(input)
when 'object' then (select count(*) from jsonb_object_keys(input))
when 'string' then 1
else 0
end
from data
returns:
input | case
---------------------+-----
{"test": 123} | 1
"123" | 1
123 | 0
[] | 0
[123] | 1
[123, 456] | 2
[123, {"test": 123}] | 2
Of course this can easily be put into a function:
create or replace function my_json_length(p_input jsonb)
returns int
as
$$
select case jsonb_typeof(p_input)
when 'array' then jsonb_array_length(p_input)
when 'object' then (select count(*) from jsonb_object_keys(p_input))
when 'string' then 1
else 0
end;
$$
language sql
immutable;
I would not name it json_length() to avoid any clashes in case Postgres decides to implement such a function in the future (although I am not aware of such a function in the SQL standard).
Note that jsonb is the recommended data type to store JSON values in Postgres.

Related

How to run procedure without parameter in Teradata

How to run procedure without parameter in Teradata
I'm trying with : call db1.proc_dbOSA()
Error Msg:
Call failed 3707: PROC_DBOSA: Syntax error, expected something
like a name or a Unicode delimited identifier between ‘(‘ and ‘)’
New Procedure with error result.
When i run only code then everything works ok.
REPLACE PROCEDURE db1.proc_dbOSA()
BEGIN
DELETE FROM db1.LOG_dbOSA;
INSERT INTO
db1.LOG_dbOSA
(StoreNo, IDX, Flow, Status, MRP, OSA_IDX, OSA_AC)
WITH a AS (
SELECT
c.StoreCode,
CAST(SUBSTRING(c.ArticleCode FROM 13 FOR 6) AS INT) AS IDX,
RpType,
CASE
WHEN c.MinimumTargetStockWrpl >= l.MinimumTargetStockWrpl THEN CAST(l.MinimumTargetStockWrpl AS INT)
WHEN c.MinimumTargetStockWrpl < l.MinimumTargetStockWrpl THEN CAST(c.MinimumTargetStockWrpl AS INT)
End AS StoreMin,
c.ValUnrestrictedStock
FROM
db1.tab1 c
INNER JOIN
(
SELECT
StoreCode,
ArticleCode,
MinimumTargetStockWrpl
FROM
db1.tab1
WHERE
ProcessingDate = CURRENT_DATE - 14
) l ON c.StoreCode = l.StoreCode AND c.ArticleCode = l.ArticleCode
WHERE
c.ProcessingDate = CURRENT_DATE AND c.MinimumTargetStockWrpl IS NOT NULL AND l.MinimumTargetStockWrpl IS NOT NULL AND l.MinimumTargetStockWrpl > 0
)
, t AS
(
SELECT
CAST(SUBSTRING(ArticleCode FROM 13 FOR 6) AS INT) AS IDX,
RpType,
ArticlesPlanner
FROM
DWH_db_V.STK_B_ARTICLE_DAY_V
WHERE
ProcessingDate = CURRENT_DATE AND StoreCode = 'DR04'
)
SELECT
a.StoreCode,
a.IDX,
t.RpType,
t.ArticlesPlanner,
a.RpType,
CASE
WHEN a.ValUnrestrictedStock > 0 THEN 1
WHEN a.ValUnrestrictedStock <= 0 THEN 0
End AS OSA_IDX,
CASE
WHEN a.ValUnrestrictedStock >= StoreMin THEN 1
WHEN a.ValUnrestrictedStock < StoreMin THEN 0
End AS OSA_AC
FROM
a
LEFT JOIN
t ON t.IDX = a.IDX;
End;
BTEQ Error:
+---------+---------+---------+---------+---------+---------+---------+----
Call proc_dbOSA;
Call proc_dbOSA;
$
* Failure 3707 Syntax error, expected something like '(' between the word
'proc_dbOSA' and ';'.
Statement# 1, Info =18
* Total elapsed time was 1 second.
Call proc_dbOSA();
* Failure 3707 PROC_DBOSA:Syntax error, expected something like a name or
a Unicode delimited identifier between '(' and ')'.
* Total elapsed time was 1 second.
Stored procedures do not support the following:
EXPLAIN and USING request modifiers within a stored procedure
EXECUTE macro statement
WITH clause within a stored procedure.
Stored procedures, as well as macros, do not support the following query logging statements:
BEGIN QUERY LOGGING
END QUERY LOGGING
FLUSH QUERY LOGGING
REPLACE QUERY LOGGING

PostgreSQL - Combine SELECT and RETURN VALUE of a Function

In my database I have a table "Datapoint" with the two columns "Id" (integer) and "Description" (character varying). Table "Datapoint"
I then have a table "Logging" with the three columns "Id" (integer), "Dt" (timestamp without timezone) and "Value" (double precision).Table "Logging"
I also have the following function:
CREATE OR REPLACE FUNCTION count_estimate(query text)
RETURNS integer AS
$BODY$ DECLARE rec record;ROWS INTEGER;BEGIN FOR rec IN EXECUTE 'EXPLAIN ' || query LOOP ROWS := SUBSTRING(rec."QUERY PLAN" FROM ' rows=([[:digit:]]+)');EXIT WHEN ROWS IS NOT NULL;END LOOP;RETURN ROWS;END $BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
This function returns the estimated count of entries that are found by a SELECT-Query, e.g. SELECT count_estimate('SELECT * FROM "Logging" WHERE "Id" = 3') would return 2.
I would now like to combine a SELECT-query on the table "Datapoint" with the return value of my function, so that my result looks like this:
ID | Description | EstimatedCount
1 | Datapoint 1 | 3
2 | Datapoint 2 | 4
3 | Datapoint 3 | 2
4 | Datapoint 4 | 1
My SELECT-query should look something like this:
SELECT
"Datapoint"."Id",
"Datapoint"."Description",
(SELECT count_estimate ('SELECT * FROM "Logging" WHERE "Logging"."Id" = "Datapoint"."Id"')) AS "EstimatedCount"
FROM
"Datapoint"
So my problem is to write a functioning SELECT-query for my purposes.
What about:
SELECT
"Datapoint"."Id",
"Datapoint"."Description",
count_estimate ('SELECT * FROM "Logging" WHERE "Logging"."Id" = "Datapoint"."Id"') AS "EstimatedCount"
FROM
"Datapoint"
You almost got it right, except that you need to supply the value of "Datapoint"."Id":
SELECT
"Datapoint"."Id",
"Datapoint"."Description",
count_estimate(
'SELECT * FROM "Logging" WHERE "Logging"."Id" = ' || "Datapoint"."Id"
) AS "EstimatedCount"
FROM "Datapoint";

Ckeck the presence of xml tag using oracle

<wbi:appData>
<wbi:content wbi:name="1st_status">
<wbi:value xsi:type="xsd:string">Success</wbi:value>
</wbi:content>
</wbi:appData>
this xml is in a table which has a column in the form of CLOB type.
I wanted to find if "wbi:value" tag exists in this xml or not ?
I tried using existsnode but in sql developer it is saying an error as to declare existsnode.
yes use existsnode:
SQL> with yourdata as (select to_clob('<wbi:event xmlns:wbi="http://foo" xmlns:xsi="http://x" xmlns:xsd="http://d">
2 <wbi:appData>
3 <wbi:content wbi:name="1st_status">
4 <wbi:value xsi:type="xsd:string">Success</wbi:value>
5 </wbi:content>
6 <wbi:content wbi:name="2nd_status">
7 <wbi:value xsi:type="xsd:string">Failure</wbi:value>
8 </wbi:content>
9 </wbi:appData>
10 </wbi:event>') c from dual)
11 select existsnode(xmltype(c), '/wbi:event/wbi:appData/wbi:content','xmlns:wbi="http://foo"') is_exist
12 from yourdata t
13 /
IS_EXIST
----------
1
ie
existsnode(xmltype(c), '/wbi:event/wbi:appData/wbi:content','xmlns:wbi="http://foo"')
1 = exists
0 = does not exist.
note that in my sample, i had two matching nodes (as i didn't filter on wbi:name). you can filter the xpath of course. eg:
/wbi:event/wbi:appData/wbi:content[#wbi:name="1st_status"]
to limit matches to the "1st_status" one
select count(*)
from clobtab
where existsNode(xmltype.createxml(clobcol),'/wbi:appData/wbi:content/wbi:value') = 1;
If it reurns more than 0 then it exists otherwise not.
So your trigger would be-
CREATE TRIGGER Tab_a
BEFORE INSERT
FOR EACH ROW
declare
xml_a xmltype;
begin
xml_a:=xmltype(:new.value);
if existsNode(xml_a,'/wbi:appData/wbi:content/wbi:value','xmlns:wbi="http://pat.namespace.com"') = 1
then
----insert ....
end if;
end;
actually you can use oracle's instr function, which is fast.
like:
where instr(field, 'wbi:value') > 0
You can use XMLEXISTS:
SELECT DESCRIPTOR_XML FROM TABLE_WITH_AN_XMLTYPE_COLUMN
WHERE
XMLEXISTS('//functions[function/arg[#name="class.name" and not(starts-with(., "com.example.apps.YouShantSeeMeClass"))]]'
PASSING BY VALUE DESCRIPTOR_XML);

How to scrape redirected URL with R

I have afunction
testFun <- function(myTeam){
print(myTeam)
teamResults <- sqlQuery(channel,paste(
"
SELECT soccer.tblResultsallMore.TEAMNAME,
sum(case when soccer.tblResultsallMore.RES='W' then 1 else 0 end) as W,
sum(case when soccer.tblResultsallMore.RES='L' then 1 else 0 end) as L,
sum(case when soccer.tblResultsallMore.RES='D' then 1 else 0 end) as D
FROM soccer.tblResultsallMore
WHERE soccer.tblResultsallMore.TEAMNAME=myTeam
GROUP BY soccer.tblResultsallMore.TEAMNAME
"))
return(teamResults) # no error if this is not returned
}
testFun("Everton")
I f I hardcode 'Everton' in the code, I get the required output
[1] "Everton"
TEAMNAME W L D
1 Everton 1734 1463 1057
but with the parameter there is an error
[1] "Everton"
[1] "42S22 207 [Microsoft][ODBC SQL Server Driver][SQL Server]Invalid column name 'myTeam'."
[2] "[RODBC] ERROR: Could not SQLExecDirect
Help much appreciated
In the code you provided the name myTeam is not replaced, it is seen as character string, and the sql statement looks for the team called myTeam.
variabel = "spam"
paste("from table select variabel")
Does not put "spam" in the sql statement inside paste. The correct syntax is:
paste("from table select ", variabel)
In your situation I would use sprintf. An example:
variabel = "spam"
sprintf("from table select %s", variable)
For more detail see the documentation of sprintf.
In regard to your remark, if there is no explicit return statement, the last evaluated expression is returned. For a discussion see:
Explicitly calling return in a function or not

How to search string to find data after end of pattern of characters (SQL DB2)

I need to find the next single event that appears after the last occurrence of the following pattern of events "5065|5373|5373". My problem is that the pattern can be in the string 1 to n times. Here's an example of the some data that I have to search through.
The events in BOLD are what i would be looking for.
5065|5373|5373|5065|5373|5373|5065|5373|5373|5509|5329|5321
5065|5373|5373|5065|5373|5373|5509|5270|5373|5373|5373|5080|5081|5013|5040|5295|5321
5065|5373|5373|5295|5323|5321
Any help would be greatly appreciated!
If you can't create a new stored procedure or UDF, here's a recursive query that'll do it for you:
WITH Recurs(id, index, token, source) as (
SELECT id, LOCATE('5065|5373|5373|', M.PATH_2), '', M.PATH_2
FROM M
UNION ALL
SELECT id, LOCATE('5065|5373|5373|', source, index + 15),
SUBSTR(source, index + 15, 4), source
FROM Recurs
WHERE index > 0)
SELECT *
FROM Recurs
WHERE index = 0
Which yields the expected:
ID INDEX TOKEN SOURCE
3 0 5295 5065|5373|5373|5295|5323|5321
2 0 5509 5065|5373|5373|5065|5373|5373|5509|5270|5373|5373|5373|5080|5081|5013|5040|5295|5321
1 0 5509 5065|5373|5373|5065|5373|5373|5065|5373|5373|5509|5329|5321
One fairly straightforward way to do this is with a recursive common table expression (CTE):
CREATE FUNCTION localutil.locatelastmatch(
searchparm VARCHAR(4000), inputparm VARCHAR(4000)
)
RETURNS SMALLINT
LANGUAGE SQL
RETURN
WITH rcurs(counter, output ) AS (
VALUES (0,0)
UNION ALL
SELECT counter+1, LOCATE(searchparm,inputparm,counter+1)
FROM rcurs
WHERE counter < LENGTH(inputparm) AND counter < 32767
)
SELECT MAX(output) FROM rcurs
;
It may not be the cheapest way to find the last matching occurrence, but it's at least a contender for it. By burying the complexity into a scalar user-defined function (UDF), you won't have to introduce the SQL recursion into every query that needs to search for the last instance of a pattern.
Here's how it works against your sample strings:
WITH originput(val) as (VALUES
('5065|5373|5373|5065|5373|5373|5065|5373|5373|5509|5329|5321'),
('5065|5373|5373|5065|5373|5373|5509|5270|5373|5373|5373|5080|5081|5013|5040|5295|5321'),
('5065|5373|5373|5295|5323|5321')
)
SELECT LENGTH(val) AS inputlength,
localutil.locatelastmatch( '5065|5373|5373|', val ) AS finaloffset,
SUBSTR(val, localutil.locatelastmatch( '5065|5373|5373|', val )
+ LENGTH( '5065|5373|5373|' ), 4) AS nextitem
FROM originput
;
INPUTLENGTH FINALOFFSET NEXTITEM
----------- ----------- --------
59 31 5509
84 16 5509
29 1 5295