DB2 length of Clob (JSON) - stored-procedures

I am generating a json inside a stored procedure as like
Declare res CLOB(5M);
Set res = (values (json_array(select json_object…
Json Looks like
[{pk: 1, name1: xyz, name: 2}, {pk: 2, name1: cvc, name2: vcc}]
At the end I Need the Information what is the length of the json, means How many entries do it have, beginning from Root.
I need something like this,
Declare counter SMALLINT;
Set Counter = xyz —should be 2
So How can I find out from res, that there are two rows?

--# SET TERMINATOR #
-- Create a generic function to deal with JSON arrays
CREATE OR REPLACE FUNCTION UNNEST_JSON (P_DOC CLOB(1M), P_PATH VARCHAR(128))
RETURNS TABLE
(
INDEX INT
, ITEM CLOB (1M)
)
DETERMINISTIC
NO EXTERNAL ACTION
BEGIN
DECLARE L_IDX INT DEFAULT 0;
L1:
WHILE TRUE DO
IF NOT JSON_EXISTS (P_DOC, P_PATH || '[' || L_IDX || ']') THEN LEAVE L1; END IF;
PIPE (L_IDX, JSON_QUERY (P_DOC, P_PATH || '[' || L_IDX || ']'));
SET L_IDX = L_IDX + 1;
END WHILE L1;
RETURN;
END
#
SELECT COUNT (1) AS ELEM_NUM
FROM TABLE (UNNEST_JSON (
-- You give your unnamed array some name you will refer in the 2-nd arg
JSON_OBJECT
(
KEY 'items'
VALUE
JSON_ARRAY
(
JSON_OBJECT (KEY 'pk' VALUE 1, KEY 'name1' VALUE 'xyz', KEY 'name' VALUE 2) FORMAT JSON
, JSON_OBJECT (KEY 'pk' VALUE 2, KEY 'name1' VALUE 'cvc', KEY 'name2' VALUE 'vcc') FORMAT JSON
)
FORMAT JSON
)
, '$.items'
))
#
ELEM_NUM
2

Related

How to Execute Macro through Stored Procedure

I have created one macro in Teradata, now wish to call that macro through Stored Procedure (in Teradata only). The SQL part belongs to macro.
I wrote this procedure in case if execution of macro is not possible through procedure.
Kindly suggest both the option.
CREATE PROCEDURE MDM_STAGE.match_sqls_proc
(
in fname VARCHAR(30),
in match_frst_name VARCHAR (100),
in lname VARCHAR(30),
in match_last_name VARCHAR (100),
in addr1 VARCHAR(1000),
in zip_cd_base varchar(10),
in email VARCHAR(30),
in phone VARCHAR(30),
out msgs VARCHAR(10)
-- INOUT errstr VARCHAR(30)
)
begin
DECLARE SQLTEXT2 VARCHAR (10) ;
DECLARE TBL_COUNT INT ;
select count (*) into :TBL_COUNT from
(
SELECT
CUST.CUST_ID,
CUST.FRST_NAME,
CUST.MATCH_FRST_NAME,
CUST.LAST_NAME,
CUST.MATCH_LAST_NAME,
CUST.GNDR_TYPE_CD,
STREET_ADDR.ADDR_LN_1_TXT,
STREET_ADDR.ADDR_LN_2_TXT,
STREET_ADDR.ADDR_LN_3_TXT,
cast (coalesce (STREET_ADDR.ADDR_LN_1_TXT,'') || ' ' || coalesce
(STREET_ADDR.ADDR_LN_2_TXT,'' ) as varchar (1000)) as addr,
STREET_ADDR.CITY_NAME,
STREET_ADDR.POSTL_CD,
STREET_ADDR.STREET_ADDR_ID,
STREET_ADDR.ADDR_SBTYPE_CD,
ELCTRNC_ADDR.ELCTRNC_ADDR_ID,
ELCTRNC_ADDR.ELCTRNC_ADDR_SBTYPE_CD,
ELCTRNC_ADDR.ELCTRNC_ADDR_TXT,
TLPHN_NUM.TLPHN_LN_NUM,
TLPHN_NUM.TLPHN_NUM_ID
FROM
MDM_STAGE.REF_CUST_V CUST
LEFT OUTER JOIN
MDM_STAGE.REF_STREET_ADDR_V STREET_ADDR
ON
CUST.CUST_ID = STREET_ADDR.CUST_ID
AND
--RDM_ADDRSIM (STREET_ADDR.ADDR_LN_1_TXT ,'2285Main Street' ) =1
RDM_ADDRSIM ( cast (coalesce (STREET_ADDR.ADDR_LN_1_TXT,'') || ' ' ||
coalesce (STREET_ADDR.ADDR_LN_2_TXT,'' ) as varchar (1000)) ,:addr1) =1
AND
SUBSTR(STREET_ADDR.POSTL_CD,0,6) = (:zip_cd_base)
LEFT OUTER JOIN
MDM_STAGE.REF_ELCTRNC_ADDR_V ELCTRNC_ADDR
ON
CUST.CUST_ID = ELCTRNC_ADDR.CUST_ID
/*AND
STREET_ADDR.ADDR_SBTYPE_CD = ELCTRNC_ADDR.ELCTRNC_ADDR_SBTYPE_CD*/
AND
ELCTRNC_ADDR.ELCTRNC_ADDR_TXT= (:email)
LEFT OUTER JOIN
MDM_STAGE.REF_TLPHN_NUM_V TLPHN_NUM
ON
CUST.CUST_ID = TLPHN_NUM.CUST_ID
/*AND
STREET_ADDR.ADDR_SBTYPE_CD = TLPHN_NUM.ADDR_SBTYPE_CD*/
AND
TLPHN_NUM.TLPHN_LN_NUM = (:phone)
WHERE
(RDM_sndx(COALESCE(CUST.Match_FRST_NAME,CUST.CUST_ID))=RDM_sndx(:match_frst_name)
AND
RDM_sndx(COALESCE(STRTOK(CUST.Match_LAST_NAME,' ',1),CUST.CUST_ID))=RDM_SNDX(:match_last_name)
)
AND
(
TLPHN_NUM.CUST_ID IS NOT NULL
OR
ELCTRNC_ADDR.CUST_ID IS NOT NULL
OR
STREET_ADDR.CUST_ID IS NOT NULL
) A
;
IF ( TBL_COUNT > 0 ) THEN SET SQLTEXT2 = 'Match' ;
ELSE
SET Msgs = 'Non-Match' ;
END IF;
end;
Error message -
SPL1027:E(L88), Missing/Invalid SQL statement'E(3707):Syntax error, expected something like an 'EXCEPT' keyword or an 'UNION' keyword or a 'MINUS' keyword between ')' and the word 'A'.'.
No idea, what to add between the word 'A' and ')'. Tried all the possibility but not successful. Not sure how to pass the value of SQL into the 'count' as later call procedure condition is based on that count only.

ActiveRecord Query on text field PostgreSQL

I have a table Products and around 58000 records in it. I run two queries are as follows.
Product.where(description: 'swiss').count
It returns 0 products out of 58000 products. But when I run query below.
Product.where.not(description: 'swiss').count
it returns 2932 products out of 58000 products. I think it should return all 58000 products, because it is the reverse of first query.
I did not understand why it returns only 2932 products.
If you have NULL values in your columns, this could happen, because NULL always compares as NULL, even to itself, but WHERE expression must be true to include a result.
'a' = 'a'; True
'a' = 'b'; False
'a' = NULL; Results in NULL (not false!)
NULL = NULL; Results in NULL (not true!)
'a' != 'a'; False
'a' != 'b'; True
'a' != NULL; NULL
e.g. consider the table null_test containing
id | str
----+-----
1 | <NULL>
2 | a
3 | b
When looking for a column equal to some value, the NULL is never equal, so this will just return row 2.
SELECT id FROM null_test WHERE str = 'a';
But the same applies looking for a column not equal to some value, the NULL is never not equal (its NULL), so this will just return row 3.
SELECT id FROM null_test WHERE str != 'a';
Thus the total of your = and != is not all the rows, because you never included the NULL rows. This is where IS NULL and IS NOT NULL come in (they work like you might expect = NULL and != NULL to work).
SELECT id FROM null_test WHERE str != 'a' OR str IS NULL;
Now you get rows 1 and 3, making this the opposite of str = 'a'.
For ActiveRecord, it treats the Ruby nil value like the SQL NULL and creates the IS NULL checks in certain situations.
NullTest.where(str: 'a')
SELECT * FROM "null_test" WHERE "str" = 'a'
NullTest.where.not(str: 'a')
SELECT * FROM "null_test" WHERE "str" != 'a'
NullTest.where(str: nil)
SELECT * FROM "null_test" WHERE "str" IS NULL
NullTest.where.not(str: nil)
SELECT * FROM "null_test" WHERE "str" IS NOT NULL
NullTest.where(str: nil).or(NullTest.where.not(str: 'a'))
SELECT * FROM "null_test" WHERE "str" IS NULL OR "str" != 'a'
You have likely many records where description is nil and those are not included in the not.
A way to include the 'nil' records as well would be...
Product.where('description <> ? OR description IS NULL', 'swiss')
Or alternatively
Product.where.not(description: 'swiss').or(Product.where(description: nil))

Execute immediate in user defined function - called in inner query

I am using a stored procedure to fetch records from an oracle database. This procedure returns paginated records building dynamic SQL query based on search inputs provided. Output 'STATUS' column is derived from user defined function 'GET_STATUS_FOR_ME'. Some derived columns are passed to this function based on this actual status of each record is calculated.
Everything works fine while not providing status as input search criteria. when status is provided it gives me the following error.
ORA-06535: statement string in EXECUTE IMMEDIATE is NULL or 0 length
ORA-06512: at "pkg_search", line 15
06535. 00000 - "statement string in %s is NULL or 0 length"
*Cause: The program attempted to use a dynamic statement string that
was either NULL or 0 length.
*Action: Check the program logic and ensure that the dynamic statement
string is properly initialized.
My user defined function is as below:
FUNCTION GET_STATUS_FOR_ME(
RECORDID IN NUMBER,
ASSIGNEDTO IN NUMBER,
LOCATIONID IN NUMBER,
TEMPUSERID IN NUMBER,
ACTUALSTATUS IN VARCHAR2)
RETURN VARCHAR2
AS
O_STATUS VARCHAR2(200 BYTE);
TEMP_QUERY VARCHAR2(200 BYTE);
I_COUNT NUMBER;
BEGIN
IF LOCATIONID IS NOT NULL AND TEMPUSERID IS NULL THEN
TEMP_QUERY := 'SELECT COUNT(*) FROM MY_TABLE WHERE COUNTRYIF = ' || TO_CHAR(LOCATIONID);
EXECUTE IMMEDIATE TEMP_QUERY INTO I_COUNT;
END IF;
. . . Other FUNCTION logic here. . .
RETURN O_STATUS;
END GET_STATUS_FOR_ME;
My procedure from which this function is being called is as below. This function is being called from inner query.
PROCEDURE SEARCH_RESULT_PROC(
I_LOGGEDINUSERID IN NUMBER,
I_CREATEDFROMDATE IN DATE,
I_CREATEDTODATE IN DATE,
I_STATUS IN VARCHAR2,
I_COUNTRYID IN NUMBER,
I_LANGUAGEID IN NUMBER,
I_OFFSET IN NUMBER,
I_LIMIT IN NUMBER,
I_ORDRBY IN VARCHAR2,
I_SORTBY IN VARCHAR2,
O_COUNT OUT NUMBER,
REF_CUS_RESULTS OUT SYS_REFCURSOR)
AS
PAG_END_ROW NUMBER;
STC_SQL_PART VARCHAR2(9999 BYTE);
DYNMC_SQL_CLAUSE_PART VARCHAR2(9999 BYTE);
BEGIN
PAG_END_ROW := I_OFFSET + I_LIMIT - 1;
STC_SQL_PART := 'select ID, REOCRDSTATUS,
(CASE
some logic here
END) LOCATIONID,
(CASE
some logic here
END) USERID from MY_TABLE MTABLE';
. . Other Logic TO build dynamic WHERE clause depending ON inputs provided. . .
IF I_DOCSTATUS IS NOT NULL THEN
DYNMC_SQL_CLAUSE_PART := DYNMC_SQL_CLAUSE_PART || ' AND UPPER(TEMPDATA.STATUS) = UPPER(''' || I_STATUS || ''')';
END IF;
FINAL_QUERY := 'SELECT ODATA.EID,
ODATA.TITLE,
ODATA.TYPE,
pkg_search.GET_STATUS_FOR_ME(ID,' || I_LOGGEDINUSERID || ',ODATA.LOCATIONID,ODATA.USERID,ODATA.REOCRDSTATUS) STATUS
FROM (SELECT ROWNUM RNUM , TEMP.* FROM ( ' || STC_SQL_PART || DYNMC_SQL_CLAUSE_PART || ' )TEMP WHERE ROWNUM <= ' || TO_CHAR(PAG_END_ROW) ||' ) ODATA WHERE ODATA.RNUM >= '|| TO_CHAR( I_OFFSET) ;
OPEN REF_CUS_RESULTS FOR FINAL_QUERY;
END SEARCH_RESULT_PROC;

Firebird and stored procedures: if exists then else

I'm trying to create a stored procedure for firebird 2.1 (this is the version which is to be used)
But am getting a bit stuck, so any help is appreciated.
The final version should compare 4 values agains the table, and retreive either the primaryid if the value exists, or create the new entry in the table, and return the new primaryid.
But I get stuck with only one value lookup, and it's not even using the variable yet.
SET TERM ^ ;
CREATE PROCEDURE TESTSP
( A Varchar(64) )
RETURNS
( RESULT Integer )
AS
BEGIN
IF (EXISTS (SELECT PRIMARYID FROM TABLENAME WHERE FIELD = 'Some string')) then
SELECT PRIMARYID FROM TABLENAME WHERE FIELD = 'Some string' into :primaryid;
result = PRIMARYID;
ELSE
INSERT INTO TABLENAME (FIELD) VALUES ('Some string');
result = gen_id(GEN_TABLEID, 0);
END^
SET TERM ; ^
I get a "Token unknown" for the Else command.
Update after responses:
Now I want to use the 4 variables and return the 4 results.
I think I need a for loop to do so, but with firebird, the for function means something else.
So what would be the way to go?
SET TERM ^ ;
CREATE PROCEDURE TESTSP
( value1 Varchar(64) )
RETURNS
( RESULT1 Integer )
AS
BEGIN
IF (EXISTS (SELECT PRIMARYID FROM TABLENAME WHERE FIELD = :value1)) then
SELECT PRIMARYID FROM TABLENAME WHERE FIELD = value1 into :result1;
ELSE BEGIN
result1 = gen_id(GEN_TABLEID, 1);
INSERT INTO TABLENAME (PRIMARYID, FIELD) VALUES (:result1, :value1);
END
suspend;
END^
SET TERM ; ^
As Tico already answered you have to use begin / end to group multiple statements in then / else part. The error abut column PRIMARYID being unknown is because you reference to it without having declared a local variable for it. Try this:
CREATE PROCEDURE TESTSP ( A Varchar(64) )
RETURNS ( RESULT Integer )
AS
BEGIN
-- initialize the result
Result = NULL;
-- check is the string already in table
SELECT PRIMARYID FROM TABLENAME WHERE FIELD = 'Some string' into :Result;
IF (Result is NULL) then
INSERT INTO TABLENAME(PRIMARYID, FIELD) VALUES(gen_id(GEN_TABLEID, 1), 'Some string') RETURNING PRIMARYID INTO :Result;
END^
I think your stored procedure should look like this:
SET TERM ^ ;
CREATE PROCEDURE TESTSP
( A Varchar(64) )
RETURNS ( result Integer )
AS
BEGIN
IF (EXISTS (SELECT PRIMARYID
FROM TABLENAME
WHERE FIELD = 'Some string')) then
SELECT PRIMARYID
FROM TABLENAME
WHERE FIELD = 'Some string'
into :result;
ELSE BEGIN
result = gen_id(GEN_TABLEID, 1);
INSERT INTO TABLENAME
(PRIMARYID, FIELD)
VALUES (:result, 'Some string');
END
END^
SET TERM ; ^
If you have multiple instruction for then and/ or else clause you must use BEGIN ... END-block!
SET TERM ^ ;
CREATE PROCEDURE TESTSP
( A Varchar(64) )
RETURNS
( RESULT Integer )
AS
BEGIN
IF (EXISTS (SELECT PRIMARYID FROM TABLENAME WHERE FIELD = 'Some string')) then
BEGIN
SELECT PRIMARYID FROM TABLENAME WHERE FIELD = 'Some string' into :primaryid;
result = PRIMARYID;
END
ELSE
BEGIN
INSERT INTO TABLENAME (FIELD) VALUES ('Some string');
result = gen_id(GEN_TABLEID, 0);
END
END^
SET TERM ; ^

how to sort groovy list values based on some criteria

I have one scenario to sort the values based domain class property. This property may acept all numeric and alphanumeric values in the format XXX-1.
def res= Book.listOrderByName()
or
def res = Book.findAll("from Book order by name")
Giving the same result and result is displaying first numbers latter alphanumeric values.
My problem is :
these values are sorted before -.
for example i have AB-1,AB-2,...AB-12.
The result is displayed as AB-1,AB-10.AB-11,AB-2,AB-3,..AB-9
I have result like:
[18001,18002,2,300,3901,42,9,AB-1,AB-10,AB-2,AB-21,AB-9]
It should display the value as:
[2,9,42,300,3901,18001,18002,AB-1,AB-2,AB-9,AB-10,AB-21]
Run this in the Groovy console:
List sort(list) {
list.sort {a, b ->
a.class == b.class ? a <=> b : a instanceof Integer ? -1 : 1
}
}
// Test the sort function
def list = [18001,18002,2,300,3901,42,9,'AB-1','AB-10','AB-2','AB-21','AB-9']
assert sort(list) == [2, 9, 42, 300, 3901, 18001, 18002, 'AB-1', 'AB-10', 'AB-2', 'AB-21', 'AB-9']

Resources