`Invalid Character Error `while executing oracle procedure using `getclobval()` - stored-procedures

The following procedure compiles successfully on ctrll + S (shows VALID) but on execution throws the error : Invalid character error while executing this procedure on SQL DEVELOPER.
I don't see anywhere I have a invalid character.
The following is the code to Create table and insert values:-
CREATE TABLE tempt( "set" VARCHAR2(1), "level" VARCHAR2(1), category VARCHAR2(3), value INT );
INSERT INTO tempt
SELECT 'A', 'Z', 'ABC', 847549 FROM dual UNION ALL
SELECT 'A', 'Y', 'ABC', 955808 FROM dual UNION ALL
SELECT 'A', 'X', 'ABC', 983462 FROM dual UNION ALL
SELECT 'A', 'Z', 'GHI', 762369 FROM dual UNION ALL
SELECT 'A', 'Y', 'DEF', 615863 FROM dual UNION ALL
SELECT 'A', 'X', 'DEF', 474257 FROM dual UNION ALL
SELECT 'B', 'Z', 'ABC', 959843 FROM dual UNION ALL
SELECT 'B', 'Y', 'ABC', 821704 FROM dual UNION ALL
SELECT 'B', 'X', 'ABC', 377211 FROM dual UNION ALL
SELECT 'B', 'Z', 'DEF', 945053 FROM dual UNION ALL
SELECT 'B', 'Y', 'DEF', 919120 FROM dual UNION ALL
SELECT 'B', 'Y', 'ABC', 821704 FROM dual UNION ALL
SELECT 'C', 'X', 'ABC', 377211 FROM dual UNION ALL
SELECT 'C', 'Z', 'DEF', 945053 FROM dual UNION ALL
SELECT 'C', 'Y', 'DEF', 919120 FROM dual UNION ALL
SELECT 'D', 'X', 'ABC', 377211 FROM dual UNION ALL
SELECT 'D', 'Z', 'DEF', 945053 FROM dual UNION ALL
SELECT 'D', 'Y', 'GHI', 919120 FROM dual UNION ALL
SELECT 'E', 'X', 'ABC', 377211 FROM dual UNION ALL
SELECT 'E', 'Z', 'DEF', 945053 FROM dual UNION ALL
SELECT 'E', 'Y', 'ABC', 919120 FROM dual UNION ALL
SELECT 'E', 'Z', 'ABC', 945053 FROM dual UNION ALL
SELECT 'E', 'Y', 'DEF', 919120 FROM dual UNION ALL
SELECT 'B', 'X', 'IJK', 326886 FROM dual
This is run inside the procedure window on the the SQL Developer/DB viz:-
CREATE OR REPLACE PROCEDURE "SCHEMA.pivot"(v_recordset out sys_refcursor)
AS
--v_recordset SYS_REFCURSOR;
v_sql long;
v_cols_1 long;
v_cols_2 clob;
BEGIN
SELECT LISTAGG( ''''||"level"||''' AS "'||"level"||'"' , ',' )
WITHIN GROUP ( ORDER BY "level" DESC )
INTO v_cols_1
FROM (
SELECT DISTINCT "level"
FROM tempt
);
SELECT DBMS_XMLGEN.CONVERT (
RTRIM (
XMLAGG (XMLELEMENT (
e,
'MAX(CASE WHEN CATEGORY = '
|| CHR (39)
|| CATEGORY
|| CHR (39)
|| ' THEN '
|| CHR (39)
|| "level"
|| CHR (39)
|| ' END) AS '
|| "level"
|| '_'
|| CATEGORY
|| '',
',')
ORDER BY 1 DESC).EXTRACT ('//text()').getclobval (),
','))
INTO v_cols_2
FROM (SELECT DISTINCT "level", CATEGORY
FROM tempt);
v_sql :=
'SELECT "set", ('|| v_cols_2 ||')
FROM
(
SELECT *
FROM tempt
PIVOT
(
MAX(value) FOR "level" IN ( '|| v_cols_1 ||' )
)
)
GROUP BY "set"
ORDER BY "set"';
v_sql := REPLACE (v_sql, ''', CHR (39));
DBMS_OUTPUT.PUT_LINE(v_sql);
OPEN v_recordset FOR v_sql;
end pivot;
EDIT 1 : I ran the query provided in the answer ,though compiled sucessfully I get a Invalid Character
Error :-
00911. 00000 - "invalid character" *Cause: identifiers may not start with any ASCII character other than letters and numbers. $#_ are also allowed after the first character. Identifiers enclosed by doublequotes may contain any character other than a doublequote. Alternative quotes (q'#...#') cannot use spaces, tabs, or carriage returns as delimiters. For all other contexts, consult the SQL Language Reference Manual.
I have seen the other posts with same question and tried out everything but doesnt seem to fix the issue. Line it shows for the error is OPEN v_recordset FOR v_sql;

Should be
SQL> CREATE OR REPLACE PROCEDURE pivot (v_recordset OUT SYS_REFCURSOR)
2 AS
3 --v_recordset SYS_REFCURSOR;
4 v_sql LONG;
5 v_cols_1 LONG;
6 v_cols_2 CLOB;
7 BEGIN
8 SELECT LISTAGG ('''' || "level" || ''' AS "' || "level" || '"', ',')
9 WITHIN GROUP (ORDER BY "level" DESC)
10 INTO v_cols_1
11 FROM (SELECT DISTINCT "level"
12 FROM tempt);
13
14 SELECT DBMS_XMLGEN.CONVERT (
15 RTRIM (
16 XMLAGG (XMLELEMENT (
17 e,
18 'MAX(CASE WHEN CATEGORY = '
19 || CHR (39)
20 || CATEGORY
21 || CHR (39)
22 || ' THEN '
23 || CHR (39)
24 || "level"
25 || CHR (39)
26 || ' END) AS '
27 || "level"
28 || '_'
29 || CATEGORY
30 || '',
31 ',')
32 ORDER BY 1 DESC).EXTRACT ('//text()').getclobval (),
33 ','))
34 INTO v_cols_2
35 FROM (SELECT DISTINCT "level", CATEGORY
36 FROM tempt);
37
38 v_sql := 'SELECT "set", ' || v_cols_2 || '
39 FROM
40 (
41 SELECT *
42 FROM tempt
43 PIVOT
44 (
45 MAX(value) FOR "level" IN ( ' || v_cols_1 || ' )
46 )
47 )
48 GROUP BY "set"
49 ORDER BY "set"';
50 v_sql := REPLACE (v_sql, ''', CHR (39));
51 DBMS_OUTPUT.PUT_LINE (v_sql);
52
53 OPEN v_recordset FOR v_sql;
54 END pivot;
55 /
Procedure created.
SQL>
Testing:
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2 l_rc SYS_REFCURSOR;
3 BEGIN
4 pivot (l_rc);
5 END;
6 /
SELECT "set", MAX(CASE WHEN CATEGORY = 'GHI' THEN 'Y' END) AS Y_GHI,MAX(CASE
WHEN CATEGORY = 'ABC' THEN 'Y' END) AS Y_ABC,MAX(CASE WHEN CATEGORY = 'ABC' THEN
'Z' END) AS Z_ABC,MAX(CASE WHEN CATEGORY = 'DEF' THEN 'Z' END) AS Z_DEF,MAX(CASE
WHEN CATEGORY = 'IJK' THEN 'X' END) AS X_IJK,MAX(CASE WHEN CATEGORY = 'GHI' THEN
'Z' END) AS Z_GHI,MAX(CASE WHEN CATEGORY = 'DEF' THEN 'X' END) AS X_DEF,MAX(CASE
WHEN CATEGORY = 'ABC' THEN 'X' END) AS X_ABC,MAX(CASE WHEN CATEGORY = 'DEF' THEN
'Y' END) AS Y_DEF
FROM
(
SELECT *
FROM tempt
PIVOT
(
MAX(value) FOR "level" IN ( 'Z' AS "Z",'Y' AS
"Y",'X' AS "X" )
)
)
GROUP BY "set"
ORDER BY "set"
PL/SQL procedure successfully completed.
SQL>
Running the resulting query produces the following result (snipped):
SQL> SELECT "set", MAX(CASE WHEN CATEGORY = 'GHI <snip>
ORY = 'DEF' THEN 'Z' END) AS Z_DEF,MAX(CASE WHEN <snip>
(CASE WHEN CATEGORY = 'ABC' THEN 'X' END) AS X_A <snip>
2 FROM
3 (
4 SELECT *
5 FROM tempt
6 PIVOT
7 (
8 MAX(value) FOR "level" IN ( 'Z'
9 )
10 )
11 GROUP BY "set"
12 ORDER BY "set"
13 /
s Y Y Z Z X Z X X Y
- - - - - - - - - -
A Y Y Z Z Z X X Y
B Y Z Z X X X Y
C Y Z Z X X Y
D Y Y Z Z Z X X Y
E Y Z Z X X Y
SQL>
P.S. Forgot to say: really, really, REALLY bad idea naming column using lower case under double quotes.

Related

Transpose rows into columns in BigQuery using standard sql [duplicate]

This question already has answers here:
How to Pivot table in BigQuery
(7 answers)
Closed 2 years ago.
Good morning,
I'm trying to transpose some data in big query. I've looked at a few other people who have asked this on stackoverflow but the way to do this seems to be to use legacy sql (using group_concat_unquoted) rather than standard sql. I would use legacy but I've had issues with nested data in the past so have since used standard only.
Here's my example, to give some context I'm trying to map out some customer journeys which I have below:
uniqueid | page_flag | order_of_pages
A | Collection| 1
A | Product | 2
A | Product | 3
A | Login | 4
A | Delivery | 5
B | Clearance | 1
B | Search | 2
B | Product | 3
C | Search | 1
C | Collection| 2
C | Product | 3
However I'd like to transpose the data so it looks like this:
uniqueid | 1 | 2 | 3 | 4 | 5
A | Collection | Product | Product | Login | Delivery
B | Clearance | Search | Product | NULL | NULL
C | Search | Collection | Product | NULL | NULL
I've tried using multiple left joins but get the following error:
select a.uniqueid,
b.page_flag as page1,
c.page_flag as page2,
d.page_flag as page3,
e.page_flag as page4,
f.page_flag as page5
from
(select distinct uniqueid,
(case when uniqueid is not null then 1 end) as page_hit1,
(case when uniqueid is not null then 2 end) as page_hit2,
(case when uniqueid is not null then 3 end) as page_hit3,
(case when uniqueid is not null then 4 end) as page_hit4,
(case when uniqueid is not null then 5 end) as page_hit5
from `mytable`) a
LEFT JOIN (
SELECT *
from `mytable`) b on a.uniqueid = b.uniqueid
and a.page_hit1 = b.order_of_pages
LEFT JOIN (
SELECT *
from `mytable`) c on a.uniqueid = c.uniqueid
and a.page_hit2 = c.order_of_pages
LEFT JOIN (
SELECT *
from `mytable`) d on a.uniqueid = d.uniqueid
and a.page_hit3 = d.order_of_pages
LEFT JOIN (
SELECT *
from `mytable`) e on a.uniqueid = e.uniqueid
and a.page_hit4 = e.order_of_pages
LEFT JOIN (
SELECT *
from `mytable`) f on a.uniqueid = f.uniqueid
and a.page_hit5 = f.order_of_pages
Error: Query exceeded resource limits for tier 1. Tier 13 or higher required.
I've looked at using Array function as well but I've never used this before and I'm not sure if this is just for transposing the other way around. Any advice would be grand.
Thank you
for BigQuery Standard SQL
#standardSQL
SELECT
uniqueid,
MAX(IF(order_of_pages = 1, page_flag, NULL)) AS p1,
MAX(IF(order_of_pages = 2, page_flag, NULL)) AS p2,
MAX(IF(order_of_pages = 3, page_flag, NULL)) AS p3,
MAX(IF(order_of_pages = 4, page_flag, NULL)) AS p4,
MAX(IF(order_of_pages = 5, page_flag, NULL)) AS p5
FROM `mytable`
GROUP BY uniqueid
You can play/test with below dummy data from your question
#standardSQL
WITH `mytable` AS (
SELECT 'A' AS uniqueid, 'Collection' AS page_flag, 1 AS order_of_pages UNION ALL
SELECT 'A', 'Product', 2 UNION ALL
SELECT 'A', 'Product', 3 UNION ALL
SELECT 'A', 'Login', 4 UNION ALL
SELECT 'A', 'Delivery', 5 UNION ALL
SELECT 'B', 'Clearance', 1 UNION ALL
SELECT 'B', 'Search', 2 UNION ALL
SELECT 'B', 'Product', 3 UNION ALL
SELECT 'C', 'Search', 1 UNION ALL
SELECT 'C', 'Collection', 2 UNION ALL
SELECT 'C', 'Product', 3
)
SELECT
uniqueid,
MAX(IF(order_of_pages = 1, page_flag, NULL)) AS p1,
MAX(IF(order_of_pages = 2, page_flag, NULL)) AS p2,
MAX(IF(order_of_pages = 3, page_flag, NULL)) AS p3,
MAX(IF(order_of_pages = 4, page_flag, NULL)) AS p4,
MAX(IF(order_of_pages = 5, page_flag, NULL)) AS p5
FROM `mytable`
GROUP BY uniqueid
ORDER BY uniqueid
result is
uniqueid p1 p2 p3 p4 p5
A Collection Product Product Login Delivery
B Clearance Search Product null null
C Search Collection Product null null
Depends on your needs you can also consider below approach (not pivot though)
#standardSQL
SELECT uniqueid,
STRING_AGG(page_flag, '>' ORDER BY order_of_pages) AS journey
FROM `mytable`
GROUP BY uniqueid
ORDER BY uniqueid
if to run with same dummy data as above - result is
uniqueid journey
A Collection>Product>Product>Login>Delivery
B Clearance>Search>Product
C Search>Collection>Product

Match self-define token in PARSE

I am working on a string-transforming problem. The requirement is like this:
line: {INSERT INTO `pub_company` VALUES ('1', '0', 'ABC大学', 'B', 'admin', '2014-10-09 11:40:44', '', '105210', null)}
==>
line: {INSERT INTO `pub_company` VALUES ('1', '0', 'ABC大学', 'B', 'admin', to_date('2014-10-09 11:40:44', 'yyyy-mm-dd hh24:mi:ss'), '', '105210', null)}
Note: the '2014-10-09 11:40:44' is transformed to to_date('2014-10-09 11:40:44', 'yyyy-mm-dd hh24:mi:ss').
My code looks like below:
date: use [digit][
digit: charset "0123456789"
[4 digit "-" 2 digit "-" 2 digit space 2 digit ":" 2 digit ":" 2 digit]
]
parse line [ to date to end]
but I got this error:
** Script error: PARSE - invalid rule or usage of rule: digit
** Where: parse do either either either -apply-
** Near: parse line [to date to end]
I have made some tests:
probe parse "SSS 2016-01-01 00:00:00" [thru 3 "S" space date to end] ;true
probe parse "SSS 2016-01-01 00:00:00" [ to date to end] ; the error above
As the position of date value is not the same in all my data set, how can I reach it and match it and make the corresponding change?
I did as below:
line: {INSERT INTO `pub_company` VALUES ('1', '0', 'ABC大学', 'B', 'admin', '2014-10-09 11:40:44', '', '105210', null)}
d: [2 digit]
parse/all line [some [p1: {'} 4 digit "-" d "-" d " " d ":" d ":" d {'} p2: (insert p2 ")" insert p1 "to_date(" ) | skip]]
>> {INSERT INTO `pub_company` VALUES ('1', '0', 'ABC??', 'B', 'admin', to_date('2014-10-09 11:40:44'), '', '105210', null)}
TO and THRU have historically not allowed arbitrary rules as their parameters. See #2129:
"The syntax of TO and THRU is currently restricted by design, for really significant performance reasons..."
This was relaxed in Red. So for example, the following will work there:
parse "aabbaabbaabbccc" [
thru [
some "a" (prin "a") some "b" (prin "b") some "c" (prin "c")
]
]
However, it outputs:
abababababc
This shows that it really doesn't have a better answer than just "naively" applying the parse rule iteratively at each step. Looping the PARSE engine is not as efficient as atomically running a TO/THRU for which faster methods of seeking exist (basic string searches, for instance). And the repeated execution of code in parentheses may not line up with what was actually intended.
Still...it seems better to allow it. Then let users worry about when their code is slow and performance tune it if it matters. So odds are that the Ren-C branch of Rebol will align with Red in this respect, and allow arbitrary rules.
I have made it by an indirect way:
date: use [digit][
digit: charset "0123456789"
[4 digit "-" 2 digit "-" 2 digit space 2 digit ":" 2 digit ":" 2 digit]
]
line: {INSERT INTO `pub_company` VALUES ('1', '0', 'ABC大学', 'B', 'admin', '2014-10-09 11:40:44', '', '105210', null)}
parse line [
thru "(" vals: (
blk: parse/all vals ","
foreach val blk [
if parse val [" '" date "'"][
;probe val
replace line val rejoin [ { to_date(} at val 2 {, 'yyyy-mm-dd hh24:mi:ss')}]
]
]
)
to end
(probe line)
]
The output:
{INSERT INTO `pub_company` VALUES ('1', '0', 'ABC大学', 'B', 'admin', to_date('2014-10-09 11:40:44', 'yyyy-mm-dd hh24:mi:ss'), '', '105210', null)}
Here a true Rebol2 solution
line: {INSERT INTO `pub_company` VALUES ('1', '0', 'ABC??', 'B', 'admin', '2014-10-09 11:40:44', '', '105210', null)}
date: use [digit space][
space: " "
digit: charset "0123456789"
[4 digit "-" 2 digit "-" 2 digit space 2 digit ":" 2 digit ":" 2 digit]
]
>> parse/all line [ some [ [da: "'" date (insert da "to_date (" ) 11 skip de: (insert de " 'yyyy-mm-dd hh24:mi:ss'), ") ] | skip ] ]
== true
>> probe line
{INSERT INTO `pub_company` VALUES ('1', '0', 'ABC??', 'B', 'admin', to_date ('2014-10-09 11:40:44', 'yyyy-mm-dd hh24:mi:ss'), '', '105210', null)}

Query expression with join on multi part key and nullables

Consider the following two tables, with 3 columns each:
Table 1:
a INTEGER NOT NULL,
b INTEGER NOT NULL,
c INTEGER NOT NULL
Table 2:
d INTEGER NOT NULL,
e INTEGER,
f INTEGER NOT NULL
I'm trying to write a query expression that joins the two tables on a 2 part, composite key: (b, c) = (e, f).
I know that if column e was not Nullable I could just write:
query {
for r1 in c.table1 do
join r2 in c.table2 on ((r1.b, r1.c) = (r2.e, r2.f))
.
.
}
But how do I do it if column e is Nullable but column b in not?

How to get a single value from a cell-range by matching multiple columns and rows

I'm struggling with this one.
Here is data from 'sheet1':
|| A B C D E
=========================================
1 || C1 C2 X1 X2 X3
.........................................
2 || a b 1 2 3
3 || a d 10 11 12
4 || c d 4 5 6
5 || c f 13 14 15
6 || e f 7 8 9
7 || e b 16 17 18
Here's data in "sheet2":
|| A B C D
=================================
1 || C1 C2 C3 | val
.................................
2 || a d X2 | ?
3 || c f X1 | ?
4 || e b X3 | ?
Note that column C in sheet2 actually has values equal to user column names in sheet1.
I simply want to match A, B and C in sheet2 with A, B and 1 in sheet1 to find values in the last column:
|| A B C D
=================================
1 || C1 C2 C3 | val
.................................
2 || a d X2 | 11
3 || c f X1 | 13
4 || e b X3 | 18
I've been playing with OFFSET() and MATCH() but can't seem to lock down on one cell using multiple search criteria. Can someone help please?
I would use this function in sheet2 D2 field:
=index(filter(sheet1!C:E,sheet1!A:A=A2,sheet1!B:B=B2),1,match(C2,sheet1!$C$1:$E$1,0))
Explanation:
There is a FILTER function which will result the X1,X2,X3 values (C,D,E columns of sheet1) of the row which matches to the these two conditions:
C1 is "a"
C2 is "d"
So it will give back an array: [10,11,12] - which is the values of the X1, X2, X3 (C,D,E ) columns of sheet1 in the appropriate row.
Then, the INDEX function will grab this array. Now we only need to determine which value to pick. The MATCH function will do this computation as it tries to find the third condition C3 (which is in this case "X2) in the header row of sheet1. And in this example it will give back "2" as X2 is in the 2nd position of sheet1!c1:e1
So the INDEX function will give back the 2nd element of this array:[10,11,12], which is 11, the desired value.
Hope this helps.

php vlookup in sql

I have a table like this in sql
ID NAME SIZE GROUP1 GROUP2 SIZE2
1 casa xl 1 2
2 casa l 1 2
I'd like to obtain a table like this
ID NAME SIZE GROUP1 GROUP2 SIZE2
1 casa xl 1 2 l
2 casa l 1 2 xl
So the value of GROUP1 and GROUP2 identify the id that have similar NAME but different value for size
Ho can I do?
Join in the same table again, with the id that is not the same as the record itself:
select
t.ID, t.NAME, t.SIZE, t.GROUP1, t.GROUP2, t2.SIZE
from
TheTable t
inner join TheTable t2 on t2.ID = case t.GROUP1 when t.ID then t.GROUP2 else t.GROUP1 end
To select from table1 and insert it into table2:
insert into table2
select
t.ID, t.NAME, t.SIZE, t.GROUP1, t.GROUP2, t2.SIZE
from
table1 t
inner join table1 t2 on t2.ID = case t.GROUP1 when t.ID then t.GROUP2 else t.GROUP1 end

Resources