Oracle pivot query results in a "command not properly ended" error - oracle

When firing the following query to a 10g Oracle database:
SELECT * FROM (
SELECT T1.ID, T2.ACCT_NO, ROW_NUMBER() OVER (PARTITION BY T1.ID ORDER BY T1.ID DESC) AS RRRRRRR
FROM TABLE1 T1
INNER JOIN TABLE T2 ON T1.ID = T2.ID
WHERE T1.ID = 666
)
PIVOT (MIN(T1.ID) AS ALIAS1 FOR RRRRRRR IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
I get an "command not properly ended" error.
I've searched for Oracle pivot examples and they all pretty much showed the same example.
What am I missing here?

As #APC pointed out there is no PIVOT function in oracle 10g, so you can use an aggregate function and a CASE, similar to this:
SELECT id, acct_no,
min(case when RRRRRRR = 1 then id end) as '1',
min(case when RRRRRRR = 2 then id end) as '2',
min(case when RRRRRRR = 3 then id end) as '3',
min(case when RRRRRRR = 4 then id end) as '4',
min(case when RRRRRRR = 5 then id end) as '5',
min(case when RRRRRRR = 6 then id end) as '6',
min(case when RRRRRRR = 7 then id end) as '7',
min(case when RRRRRRR = 8 then id end) as '8',
min(case when RRRRRRR = 9 then id end) as '9',
min(case when RRRRRRR = 10 then id end) as '10'
FROM
(
SELECT T1.ID, T2.ACCT_NO, ROW_NUMBER() OVER (PARTITION BY T1.ID ORDER BY T1.ID DESC) AS RRRRRRR
FROM TABLE1 T1
INNER JOIN TABLE T2 ON T1.ID = T2.ID
WHERE T1.ID = 666
) x
GROUP BY id, acct_no

Your syntax is impeccable. Unfortunately PIVOT was introduced in Oracle 11g and you're using 10g.

Related

How to use GREATEST function with Over Partition by in Oracle

In the below code I want to select customer_name, location, gender and address along with customerid, aread_code.
select
customerid, aread_code, GREATEST(MAX(productid), MAX(itemid))
from
CUSTOMER C
inner join
ORDER O ON c.custid = o.custid
where
c.custtype = 'EXECUTIVE'
group
customerid, by aread_code;
I tried GREATEST function along with OVER PARTITION BY to display required columns. It's throwing an error.
Could you please help me to select the required columns.
Thank you.
DISCLAIMER:
When working with more than one table, qualify the columns with their table name. You haven't done so, so we don't know what of the two tables the aread_code resides in. In my answer here I assume it is the customer's area. If it isn't then you need a different answer.
ANSWER:
You group by customer_id and area code. This gives you one row per customer. And you want the maximum product/item ID from the orders table. (I suppose they are drawn from the same sequence, so you can use this ID somehow to go on from there.)
The easiest approach for this is to get the maximum ID in a subquery. Either directly in the select clause or in the from clause.
Here is how to do this in the SELECT clause:
select
c.*,
(
select greatest(max(productid), max(itemid))
from orders o
where o.custid = c.custid
) as max_id
from customer c
where c.custtype = 'EXECUTIVE';
Here is one way to do this in the FROM clause:
select
c.*,
agg.max_id
from customer c
outer apply
(
select greatest(max(productid), max(itemid)) as max_id
from orders o
where o.custid = c.custid
) agg
where c.custtype = 'EXECUTIVE';
And here is another way to do this in the FROM clause:
select
c.*,
agg.max_id
from customer c
left outer join
(
select
custid,
greatest(max(productid), max(itemid)) as max_id
from orders
group by custid
) agg on agg.custid = c.custid
where c.custtype = 'EXECUTIVE';
If you only want customers with at least one order, then I recommend the approach with the FROM clause. You'd have to turn the OUTER APPLY into a CROSS APPLY resp. the LEFT OUTER JOIN into an INNER JOIN for this.
There are several mistakes in your code. The main confusion is not using table alias prefix for columns. There is a group by mistake and a problem with your table name ORDER - if it is a name of a table. ORDER is a reserved word in Oracle and if it is the name of the table then you should use something like "YOUR_OWNER_NAME"."ORDER".... Here is the corected code with some sample data and result:
WITH
customers (CUSTID, PRODUCTID, AREAD_CODE, CUSTOMER_NAME, LOCATION, GENDER, ADDRESS, CUSTTYPE) AS
(
Select 1, 1, 63, 'Name 1', 'Location 1', 'M', 'Address 1', 'EXECUTIVE' From Dual Union All
Select 2, 1, 63, 'Name 1', 'Location 1', 'M', 'Address 1', 'EXECUTIVE' From Dual Union All
Select 3, 3, 63, 'Name 1', 'Location 1', 'M', 'Address 1', 'EXECUTIVE' From Dual Union All
Select 4, 7, 63, 'Name 1', 'Location 1', 'M', 'Address 1', 'EXECUTIVE' From Dual
),
orders (ORDER_ID, CUSTID, ITEMID, SOME_COLUMN) AS
(
Select 1, 1, 1, 'Some other data' From Dual Union All
Select 2, 2, 1, 'Some other data' From Dual Union All
Select 3, 3, 1, 'Some other data' From Dual Union All
Select 4, 3, 3, 'Some other data' From Dual Union All
Select 5, 4, 1, 'Some other data' From Dual Union All
Select 6, 4, 8, 'Some other data' From Dual
)
select
c.custid, c.aread_code, GREATEST(MAX(c.productid), MAX(o.itemid)) "MAX_ID"
from
CUSTOMERS C
inner join
ORDERS O ON c.custid = o.custid
where
c.custtype = 'EXECUTIVE'
group by
c.custid, c.aread_code
CUSTID AREAD_CODE MAX_ID
---------- ---------- ----------
1 63 1
4 63 8
3 63 3
2 63 1
There are different options to get the rest of the columns depending on your actual data you could use some or all of them.
Option 1 - select and group by as suggested in Beefstu's comment below
select Distinct
c.custid, c.customer_name, c.location, c.address, c.gender, c. custtype, c.aread_code,
GREATEST(MAX(c.productid), MAX(o.itemid)) "MAX_ID"
from
CUSTOMERS C
inner join
ORDERS O ON c.custid = o.custid
where
c.custtype = 'EXECUTIVE'
group by
c.custid, c.customer_name, c.location, c.address, c.gender, c. custtype, c.aread_code
order by c.custid
CUSTID CUSTOMER_NAME LOCATION ADDRESS GENDER CUSTTYPE AREAD_CODE MAX_ID
---------- ------------- ---------- --------- ------ --------- ---------- ----------
1 Name 1 Location 1 Address 1 M EXECUTIVE 63 1
2 Name 1 Location 1 Address 1 M EXECUTIVE 63 1
3 Name 1 Location 1 Address 1 M EXECUTIVE 63 3
4 Name 1 Location 1 Address 1 M EXECUTIVE 63 8
Option 2. - using analytic functions MAX() OVER() with Distinct keyword (could be performance costly with big datasets) - result is the same as above
select Distinct
c.custid, c.customer_name, c.location, c.address, c.gender, c. custtype, c.aread_code,
GREATEST(MAX(c.productid) OVER(Partition By c.custid), MAX(o.itemid) OVER(Partition By c.custid)) "MAX_ID"
from
CUSTOMERS C
inner join
ORDERS O ON c.custid = o.custid
where
c.custtype = 'EXECUTIVE'
order by c.custid
Option 3 - using left join to a subquery - see the solution offered by Thorsten Kettner

Oracle Hierarchical Queries

I am struggling with a query with the below requirement:
Table A
ID Name Key
1 A1 Key1
2 A2 Key2
3 A3 Key3
Table B
ID A_ID NAME CONTAINER_A_ID
1 1 B1 NULL
2 1 B2 NULL
3 1 B3 2
4 2 B4 NULL
5 2 B5 NULL
6 3 B6 NULL
7 3 B7 NULL
The Key column in table A is unique
The A_ID column in table B is a foreign key of table A
The CONTAINER_A_ID column in table B means the row in table B can be a
container, it contains other data rows indicated by the CONTAINER_A_ID value.
Below is the example:
the input parameter is table A key column value, let's say A.Key = 'key1', and the result based on the above sample data will be:
A.ID A.NAME A.KEY B.ID B.A_ID B.NAME B.CONTAINER_A_ID
1 A1 KEY1 1 1 B1 NULL
1 A1 KEY1 2 1 B2 NULL
1 A1 KEY1 3 1 B3 2
2 A2 KEY2 4 2 B4 NULL
2 A2 KEY2 5 2 B5 NULL
if the input parameter is A.Key = 'key2', then the result will be:
A.ID A.NAME A.KEY B.ID B.A_ID B.NAME B.CONTAINER_A_ID
2 A2 KEY2 4 2 B4 NULL
2 A2 KEY2 5 2 B5 NULL
Thanks
This is on Oracle 11g.
If you are specifically looking for CONNECT BY I am not aware of that yet.
drop table t1; drop table t2;
create table t1 (id int primary key, name char(5), key char(5));
create table t2 (id int primary key, a_id int, name char(5) , container int);
insert into t1 values (1, 'A1', 'K1');
insert into t1 values (2, 'A2', 'K2');
insert into t1 values (3, 'A3', 'K3');
insert into t2 values (1, 1, 'B1', null);
insert into t2 values (2, 1, 'B2', null);
insert into t2 values (3, 1, 'B3', 2);
insert into t2 values (4, 2, 'B4', null);
insert into t2 values (5, 2, 'B5', null);
insert into t2 values (6, 3, 'B6', null);
insert into t2 values (7, 3, 'B7', null);
with t(id, name, key, bid, aid, bname, con) as (
select a.id, a.name, a.key, b.id, b.a_id, b.name, b.container
from t1 a
inner join
t2 b
on a.id = b.a_id
where a.key = 'K1'
union all
select a.id, a.name, a.key, b.id, b.a_id, b.name, b.container
from t t
inner join
t1 a
on a.id = t.con
inner join
t2 b
on a.id = b.a_id
) select * from t;
EDIT: Reponse to Jorge's comment
insert into t2 values (4, 2, 'B4', 3);
This is for Hierarchical Query
with TableA as
(
select 1 id, 'A1' Name, 'Key1' key from dual union all
select 2, 'A2', 'Key2' from dual union all
select 3, 'A3', 'Key3' from dual
)
, tableb as
(
select 1 id, 1 a_id, 'B1' name , null CONTAINER_A_ID from dual union all
select 2 , 1 , 'B2' , null from dual union all
select 3 , 1 , 'B3' , 2 from dual union all
select 4 , 2 , 'B4' , null from dual union all
select 5 , 2 , 'B5' , null from dual union all
select 6 , 3 , 'B6' , null from dual union all
select 7 , 3 , 'B7' , null from dual
)
select
a.id, a.name, a.key, b.id, b.a_id, b.name, b.container_a_id
from
tableb b
left join
tablea a
on
a.id = b.a_id
start with
A.Key = 'Key1'
connect by
prior b.container_a_id = b.a_id;
If you need order then add order by a.id, b.id,a.name,...; to the end.
CTEs are fine to use in 11g Oracle. I just saw Jorge is in before me. It is easier to see how the recursion works if you just use tableB in the CTE and then join to the CTE to get all the fields, like this
with
recurse (a_id, b_id, parent_id)
as
(select a_id, id, container_a_id as parent_id
from tableB
WHERE A_ID = 1 -- Put your parameter here
union all
select b.a_id, b.id, b.container_a_id
from recurse r, tableB b
where b.a_id = r.parent_id
)
select r.a_id, a.name, a.key, b.id, b.a_id, b.name, b.container_a_id
from recurse r, tableA a, tableB b
where r.a_id = a.id and r.b_id = b.id
;
This gets the same results, but although you have to use a_id and not a_key for the condition, it is a little bit easier to understand the recursion.
So, leaving this here in case it helps someone to understand a bit about CTEs.

Alternatives to CASE in Oracle 8i [duplicate]

This question already has answers here:
Query works on Oracle 11g but fails on Oracle 8i
(2 answers)
Closed 9 years ago.
Thanks to the stackoverflow community I found out that this query is not supported in Oracle 8i because of the CASE WHEN.
select (case
when seqnum = 1 then
'1'
when seqnum = cnt then
'0'
end) as value1,
(case
when seqnum = 1 then
t.BEGIN_DT
when seqnum = cnt then
t.END_DT
end) as TIME1,
t4.UNIT1 || '.SUBBATCH_TRIGGER' TAG
from (select t.*,
row_number() over(partition by t.BATCH_ID, t.plant_unit, t3.ID2 order by t.BEGIN_DT) as seqnum,
count(*) over(partition by t.BATCH_ID, t.plant_unit, t3.ID2) as cnt
from SCH2.tb_pg_unit_stap t
join (select ID1,batch_id from SCH2.VW_BATCH) t2 on t.BATCH_ID = t2.BATCH_ID
join (select ID2,ID1 from SCH1.STEP) t3 on t3.ID1 = t2.ID1) t
join SCH2.TB_W_MACHINE t4 on t4.plant_unit = t.plant_unit
where (seqnum = 1
or seqnum = cnt) AND (t.BEGIN_DT > '01-jan-2013' AND t.BEGIN_DT < '01-feb-2013');
What are the alternatives for this query to run on Oracle 8i?
Thanks in advance!
You should try with decode(..., ..., ...)
select
decode(seqnum,
1 , '1',
cnt, '0'
) as value1,
decode(seqnum,
1 , t.BEGIN_DT,
cnt, t.END_DT
) as TIME1
...
here's the link to the documentation of decode.
However, as has been pointed out in a comment, the join construct (ansi joins) won't work in 8i either.
instead of (case when seqnum = 1 then '1' when seqnum = cnt then '0' end)
you could use decode(seqnum, 1, '1', decode(seqnum, cnt, '0'))
I hope you get the idea

Query works on Oracle 11g but fails on Oracle 8i

I am running this query in Oracle 11g with no problem:
select (case
when seqnum = 1 then
'1'
when seqnum = cnt then
'0'
end) as value1,
(case
when seqnum = 1 then
t.BEGIN_DT
when seqnum = cnt then
t.END_DT
end) as TIME1,
t4.UNIT1 || '.SUBBATCH_TRIGGER' TAG
from (select t.*,
row_number() over(partition by t.BATCH_ID, t.plant_unit, t3.ID2 order by t.BEGIN_DT) as seqnum,
count(*) over(partition by t.BATCH_ID, t.plant_unit, t3.ID2) as cnt
from SCH2.tb_pg_unit_stap t
join (select ID1,batch_id from SCH2.VW_BATCH) t2 on t.BATCH_ID = t2.BATCH_ID
join (select ID2,ID1 from SCH1.STEP) t3 on t3.ID1 = t2.ID1) t
join SCH2.TB_W_MACHINE t4 on t4.plant_unit = t.plant_unit
where (seqnum = 1
or seqnum = cnt) AND (t.BEGIN_DT > '01-jan-2013' AND t.BEGIN_DT < '01-feb-2013');
But when I run it in Oracle 8i (8.7.1) it gives ORA-00933: SQL command not properly ended.
Is there anys special consideration I must know for running sql commands in Oracle 8i?
Thanks in advance
AFAK case command was introduced since 9i.
OK - trip--down-memory-lane time:
select decode(seqnum,
1, '1',
cnt, '0') as value1,
decode(seqnum,
1, t.BEGIN_DT,
cnt, t.END_DT) as TIME1,
t4.UNIT1 || '.SUBBATCH_TRIGGER' TAG
from (select t.*,
row_number() over (partition by t.BATCH_ID, t.plant_unit, t3.ID2
order by t.BEGIN_DT) as seqnum,
count(*) over (partition by t.BATCH_ID, t.plant_unit, t3.ID2) as cnt
from SCH2.tb_pg_unit_stap t
INNER join (select ID1, batch_id
from SCH2.VW_BATCH) t2
on t.BATCH_ID = t2.BATCH_ID
INNER join (select ID2, ID1
from SCH1.STEP) t3
on t3.ID1 = t2.ID1) t
INNER join SCH2.TB_W_MACHINE t4
on t4.plant_unit = t.plant_unit
where (seqnum = 1 or seqnum = cnt) AND
(t.BEGIN_DT > '01-jan-2013' AND t.BEGIN_DT < '01-feb-2013');

Multiple select for aleady joined table

Below is a part of my select query. In the same query I am selecting COLUMN_1 from a table TABLE2 with condition check. Also I am joining this table at end with one of the condition in the inner select as below. Can we have any other way to handle this situation with out using multiple `SELECT inside.
SELECT
T1.COLUMN_1
, (SELECT COLUMN_1 FROM TABLE2 WHERE COLUMN_22 ='A' AND COLUMN_11=T2.COLUMN_11)
, T1.COLUMN_2
, (SELECT COLUMN_1 FROM TABLE2 WHERE COLUMN_22 ='B' AND COLUMN_11=T2.COLUMN_11)
, T1.COLUMN_3
, (SELECT COLUMN_1 FROM TABLE2 WHERE COLUMN_22 ='C' AND COLUMN_11=T2.COLUMN_11)
, T1.COLUMN_4
, (SELECT COLUMN_1 FROM TABLE2 WHERE COLUMN_22 ='D' AND COLUMN_11=T2.COLUMN_11)
, T1.COLUMN_5
, (SELECT COLUMN_1 FROM TABLE2 WHERE COLUMN_22 ='E' AND COLUMN_11=T2.COLUMN_11)
, T1.COLUMN_6
, (SELECT COLUMN_1 FROM TABLE2 WHERE COLUMN_22 ='F' AND COLUMN_11=T2.COLUMN_11)
FROM TABLE1 T1, TABLE2 T2
-- plus two more tables
--plus some other conditions
WHERE T1.COLUMN_11=T2.COLUMN_11
Use CASE instead:
SELECT T1.COLUMN_1
,CASE
WHEN T2.COLUMN_22 = 'A'
THEN T2.COLUMN_1
END
,T1.COLUMN_2
,CASE
WHEN T2.COLUMN_22 = 'B'
THEN T2.COLUMN_1
END
,T1.COLUMN_3
,CASE
WHEN T2.COLUMN_22 = 'C'
THEN T2.COLUMN_1
END
,T1.COLUMN_4
,CASE
WHEN T2.COLUMN_22 = 'D'
THEN T2.COLUMN_1
END
,T1.COLUMN_5
,CASE
WHEN T2.COLUMN_22 = 'E'
THEN T2.COLUMN_1
END
,T1.COLUMN_6
,CASE
WHEN T2.COLUMN_22 = 'F'
THEN T2.COLUMN_1
END
FROM TABLE1 T1
INNER JOIN TABLE2 T2 ON T1.COLUMN_11 = T2.COLUMN_11;
EDIT
I changed the query to use the ansi join syntax. But that change is irrelevant to what you are asking. You can keep your join syntax if you want. The only relevant change is in the SELECT portion of the query.
You won't have very clean solutions I think. Another possibility is with an inner join by case:
SELECT
T1.COLUMN_1,
T2_1.COLUMN_1,
T1.COLUMN_2,
T2_2.COLUMN_1
T1.COLUMN_3,
T2_3.COLUMN_1
T1.COLUMN_4,
T2_4.COLUMN_1
T1.COLUMN_5,
T2_5.COLUMN_1
T1.COLUMN_6,
T2_6.COLUMN_1
FROM TABLE1 T1
INNER JOIN TABLE2 T2_1 ON T1.COLUMN_11=T2_1.COLUMN_11 AND T2_1.COLUMN_22 = 'A',
INNER JOIN TABLE2 T2_2 ON T1.COLUMN_11=T2_2.COLUMN_11 AND T2_2.COLUMN_22 = 'B',
INNER JOIN TABLE2 T2_3 ON T1.COLUMN_11=T2_3.COLUMN_11 AND T2_3.COLUMN_22 = 'C',
INNER JOIN TABLE2 T2_4 ON T1.COLUMN_11=T2_4.COLUMN_11 AND T2_4.COLUMN_22 = 'D',
INNER JOIN TABLE2 T2_5 ON T1.COLUMN_11=T2_5.COLUMN_11 AND T2_5.COLUMN_22 = 'E',
INNER JOIN TABLE2 T2_6 ON T1.COLUMN_11=T2_6.COLUMN_11 AND T2_6.COLUMN_22 = 'F',
WHERE
etc...

Resources