Null Replacement in Oracle Pivot Table - oracle

I am currently using the following code to pivot a table and it works perfectly. Now I want to replace any null values with 'No Data' after it is summed but I am getting errors, so I think I am placing the case statement in the wrong place.
This works:
SELECT *
FROM (SELECT PROV_NO, DATA_YEAR, DATA_MONTH, MEASURE_ID, CASES
FROM pivot_test_2)
PIVOT (SUM(CASES) FOR (MEASURE_ID) IN ('MORT_30_AMI', 'MORT_30_HF', 'MORT_30_PN'))
order by PROV_NO, DATA_YEAR, DATA_MONTH;
but this does not
SELECT *
FROM (SELECT PROV_NO, DATA_YEAR, DATA_MONTH, MEASURE_ID, CASES
FROM pivot_test_2)
PIVOT (SUM(CASES) FOR (MEASURE_ID) IN ('MORT_30_AMI', 'MORT_30_HF', 'MORT_30_PN'))
case when MORT_30_HF is null then 'No Data' else MORT_30_HF end
order by PROV_NO, DATA_YEAR, DATA_MONTH;
I get "ORA-00933: SQL command not properly ended" as the error. I'm trying to place ";" around but the error is still the same. I am currently in Oracle 11g and using Golden as my scripting/retrieval software.

You can move the CASE statement to the SELECT statement and handle the NULL values there. Better yet, use COALESCE. But unfortunately you have to do this for each item in the SELECT list:
SELECT
--Must manually reference each column.
COALESCE(TO_CHAR(MORT_30_AMI), 'No Data') MORT_30_AMI,
COALESCE(TO_CHAR(MORT_30_HF), 'No Data') MORT_30_HF,
COALESCE(TO_CHAR(MORT_30_PN), 'No Data') MORT_30_PN,
PROV_NO, DATA_YEAR, DATA_MONTH
FROM (SELECT PROV_NO, DATA_YEAR, DATA_MONTH, MEASURE_ID, CASES
FROM pivot_test_2)
PIVOT
(
SUM(CASES)
FOR (MEASURE_ID) IN
--Use aliases to make the columns easier to use.
('MORT_30_AMI' MORT_30_AMI, 'MORT_30_HF' MORT_30_HF, 'MORT_30_PN' MORT_30_PN))
ORDER BY PROV_NO, DATA_YEAR, DATA_MONTH;
A Simpler Version That Doesn't Work
Ideally you would be able to replace this part of the code:
SUM(CASES)
With this:
COALESCE(TO_CHAR(SUM(CASES)), 'No data')
Then you wouldn't need to handle each column separately. But there doesn't appear to be a way to automatically apply a non-aggregate function to the results of a PIVOT. Using the above code generates this error message:
ORA-56902: expect aggregate function inside pivot operation
Sample Schema
create table pivot_test_2
(
PROV_NO CHAR(6),
DATA_YEAR NUMBER(4),
DATA_MONTH Number(2),
MEASURE_ID VARCHAR2(250),
CASES NUMBER
);
insert into pivot_test_2
select 'A', 2000, 1, 'MORT_30_AMI', 1 from dual union all
select 'A', 2000, 1, 'MORT_30_AMI', 1 from dual union all
select 'A', 2000, 1, 'MORT_30_HF', 2 from dual union all
select 'A', 2000, 1, 'MORT_30_HF', 2 from dual;

Thanks everyone, with help from you all, I was able to cob this together and it works.
SELECT PROV_NO, DATA_YEAR, DATA_MONTH,
case when MORT_30_AMI is null then 'No Data' else to_char(MORT_30_AMI) end as MORT_30_AMI,
case when MORT_30_HF is null then 'No Data' else to_char(MORT_30_HF) end as MORT_30_HF,
case when MORT_30_PN is null then 'No Data' else to_char(MORT_30_PN) end as MORT_30_PN
FROM pivot_test_2
PIVOT (SUM(CASES) FOR (MEASURE_ID) IN ('MORT_30_AMI' as MORT_30_AMI,'MORT_30_HF' as MORT_30_HF, 'MORT_30_PN' as MORT_30_PN))
order by PROV_NO, DATA_YEAR, DATA_MONTH;

Use DECODE keyword for the query defined fields (from the list of pivot defined fields) and then replace NULL with whatever value you need. i.e., instead of NULL replace it with 'No Data'. I believe that should solve this issue.

Related

Invalid number exception when using CASE in view?

There is a table called USER_SETUP. This table has a column called EMPLOYEE_ID which is originally VARCHAR. I created a view to convert the VARCHAR data type to NUMBER. I used TO_DECIMAL("EMPLOYEE_ID",12,0) AS "EMPLOYEE_ID" (because when I use TO_INTEGER("EMPLOYEE_ID") AS "EMPLOYEE_ID", I am getting exception in view).
The view is created using nested SQL statement [e.g: select * from (select * from table)] The view is generating correct output with correct data. In the view I created 2 columns using CASE function which is based on the SQL code inside. Refer the code of SQL View below
Main View code:
CREATE VIEW ECLINIC_KNG.VIEW_USER_SETUP AS
SELECT
USER_ID,
EMPLOYEE_ID,
CASE
WHEN EMPLOYEE_ID < 50000 THEN 'Kuwaiti'
WHEN EMPLOYEE_ID >= 50000 AND LENGTH(EMPLOYEE_ID) <=6 THEN 'Non-Kuwaiti'
ELSE 'NOT KNG'
END AS "KWT_NKWT",
CASE
WHEN LENGTH(EMPLOYEE_ID) <=6 THEN 'KNG'
WHEN LENGTH(EMPLOYEE_ID)=12 THEN 'MOH'
ELSE 'Undefined'
END AS "KNG_MOH",
USER_NAME,
ACTIVE_DATE,
DEACTIVE_DATE,
ACTIVE_STATUS,
USER_CODE,
USER_LABEL,
USER_PASSWORD,
USER_TYPE,
OFFICE_ID,
USER_DESIG,
USER_LICENSE,
USER_SIGN,
CLIMIT_DAYS,
EDIT_BILL_SERVICE,
PASSWORDGROUP_ID,
USERLABEL_AR,
DISCOUNT_APPROVAL,
DISCOUNT_TYPE,
DISCOUNT_MAX,
BILL_CANCELLATION,
BILL_CANCELLATION_DAYS,
DOCTOR_ID
FROM
(SELECT
"USER_ID",
--TO_INTEGER("EMPLOYEE_ID") AS "EMPLOYEE_ID" ,
TO_DECIMAL("EMPLOYEE_ID",12,0) AS "EMPLOYEE_ID",
"USER_NAME",
"ACTIVE_DATE",
"DEACTIVE_DATE",
"ACTIVE_STATUS",
"USER_CODE",
"USER_LABEL",
"USER_PASSWORD",
"USER_TYPE",
"OFFICE_ID",
"USER_DESIG",
"USER_LICENSE",
"USER_SIGN",
"CLIMIT_DAYS",
"EDIT_BILL_SERVICE",
"PASSWORDGROUP_ID",
"USERLABEL_AR",
"DISCOUNT_APPROVAL",
"DISCOUNT_TYPE",
"DISCOUNT_MAX",
"BILL_CANCELLATION",
"BILL_CANCELLATION_DAYS",
"DOCTOR_ID"
FROM
"ECLINIC_KNG"."USER_SETUP"
WHERE
"USER_SETUP"."EMPLOYEE_ID" not in ('undefined', '299450','34(NEW RECRUIT)', 'army', 'MOH
Nurse','NEW RECRUITMENT 380','1111111','0')
AND
"USER_SETUP"."EMPLOYEE_ID" LIKE_REGEXPR '[0-9]'
ORDER BY
"USER_ID");
The 2 new columns created in the view
CASE
WHEN EMPLOYEE_ID < 50000 THEN 'Kuwaiti'
WHEN EMPLOYEE_ID >= 50000 AND LENGTH(EMPLOYEE_ID) <=6 THEN 'Non-Kuwaiti'
ELSE 'NOT KNG'
END AS "KWT_NKWT",
CASE
WHEN LENGTH(EMPLOYEE_ID) <=6 THEN 'KNG'
WHEN LENGTH(EMPLOYEE_ID)=12 THEN 'MOH'
ELSE 'Undefined'
END AS "KNG_MOH"
The issue is when I am filtering specific data using these 2 SQL scripts
--(1)
SELECT * FROM VIEW_USER_SETUP vus WHERE vus.KNG_MOH LIKE 'MOH'; --Issue
--(2)
SELECT * FROM VIEW_USER_SETUP vus WHERE vus.KWT_NKWT LIKE 'NOT KNG'; --Issue
The first issue the output is as follows:
SAP DBTech JDBC: [339]: invalid number: exception 71000339: SQL Error
The second issue the output is a follows:
invalid number: [6930] attribute value is not a number;exception 70006930: attribute value is not a number
To temporary solve 1, I use the below code:
SELECT * FROM VIEW_USER_SETUP vus WHERE vus.KNG_MOH NOT LIKE 'KNG';
Similarly to temporarily solve 2, I use the same above code because of the realtion.
I believe the issue is originating from the two CASE expressions created in the main view code, because when I use other filters from view there is no issue.
What is the correction required in the Main View code such that, when I execute --(1) and --(2), I get the required output?
Require help.

Insert issue while trying with not exist operator in oracle

Trying to insert values if particular column value not exist in table
I have tried with sub query in where statement
INSERT
INTO ANIMALDATA VALUES
(
( SELECT MAX(first)+1 FROM ANIMALDATA
)
,
'Animals',
'Lion',
10,
'',
'13-06-2019',
'STOP'
)
where not exists
(select NAMES from ANIMALDATA where NAMES='Lion');
If the lion not exist then do insert statement should run
Give me an idea what i am missing as i am a beginner to oracle queries. help me to proceed further. thanks in advance
Since you have a condition, I think you need to do an INSERT INTO...SELECT:
(UPDATE: the CREATE TABLE statement is there to provide simple test data. It is not part of the solution).
create table animaldata(first, kingdom, names, num, nl, dte, s) as
select 1, 'Animals', 'Tiger', 11, 'a', '13-06-2019', 'STOP' from dual;
INSERT
INTO ANIMALDATA select
( SELECT MAX(first)+1 FROM ANIMALDATA
)
,
'Animals',
'Lion',
10,
'',
'13-06-2019',
'STOP'
from dual
where not exists
(select NAMES from ANIMALDATA where NAMES='Lion');
Best regards,
Stew Ashton
please try below. Thanks,
INSERT
INTO ANIMALDATA a
select
( SELECT MAX(first)+1 FROM ANIMALDATA
)
,
'Animals',
'Lion',
10,
'',
'13-06-2019',
'STOP'
from dual
where not exists
(select 1 from ANIMALDATA b where b.NAMES='Lion' and a.NAMES = b.NAMES );
First off, don't use max(<value>) + 1 to come up with new values for a column - that does not play well with concurrent sessions.
Instead, you should create a sequence and use that in your inserts.
Next, if you are trying to do an upsert (update the row if it exists or insert if it doesn't), you could use a MERGE statement. In this case, you're trying to insert a row if it doesn't already exist, so you don't need the update part.
Therefore you should be doing something like:
CREATE SEQUENCE animaldata_seq
START WITH <find MAX VALUE OF animaldata.first>
INCREMENT BY 1
MAXVALUE 9999999999999999
CACHE 20
NOCYCLE;
MERGE INTO animaldata tgt
USING (SELECT 'Animals' category,
'Lion' animal,
10 num_animals,
NULL unknown_col,
TRUNC(SYSDATE) date_added,
'STOP' action
FROM dual) src
ON (tgt.animal = src.animal)
WHEN NOT MATCHED THEN
INSERT (<list of animaldata columns>)
VALUES (animaldata_seq.nextval,
src.animal,
src.unknown_col,
src.date_added,
src.action);
Note that I have tried to specify the columns being inserted into - that's good practice! Code that has insert statements that don't list the columns being inserted into are prone to errors should someone add a column to the table.
I have also assumed that the column you're adding the date into is of DATE datatypee; I have used sysdate (truncated to remove the time part) as the value to insert, but you may which to use a specific date, in which case you should use to_date(<string date>, '<string date format')

oracle 10g select with group by throwing exception with same number of columns

select
case when exists (select "CreatedOn" dategn from "RentedVehicle" where "fkTempVehicleId"=3)
then
case when exists (select "CreatedOn" dategn from "Transfer" where "fkTempVehicleId"=3)
then
(select
case when rent.dategn>tran.dategn then rent.dategn else tran.dategn end dategn,
case when rent.dategn>tran.dategn then rent.fuel else tran.fuel end fuel
from
(
select max("CreatedOn") dategn,"CheckInFuel" fuel
from "RentedVehicle" where "fkTempVehicleId"=3
group by "RentedVehicle"."CheckInFuel"
) rent ,
(select max("CreatedOn") dategn,"fkFuelLevelId" fuel
from "Transfer" where "fkTempVehicleId"=3 group by "Transfer"."fkFuelLevelId") tran)
else
(select
max("CreatedOn") dategn,"CheckInFuel" fuel
from "RentedVehicle" where "fkTempVehicleId"=3 group by "RentedVehicle"."CheckInFuel") end
else
case when exists (select "CreatedOn" dategn from "Transfer" where "fkTempVehicleId"=3)
then
(select max("CreatedOn") dategn,"fkFuelLevelId" fuel
from "Transfer" where "fkTempVehicleId"=3 group by "Transfer"."fkFuelLevelId")
else
(select "CreatedOn" dategn,"InitialFuel" fuel from "TempVehicle" where "pkTempVehicleId"=3)
end end from dual;
it is throwing exception
ORA-00913: too many values
00913. 00000 - "too many values"
*Cause:
*Action:
Error at Line: 6 Column: 2
i am sure i am missing something but i tried almost everything.
please help and elaborate what i missed
Your inline select (part of the case statement) returns two values (dategn and fuel), where only one is allowed:
...
then
(select
case when rent.dategn>tran.dategn then rent.dategn else tran.dategn end dategn,
case when rent.dategn>tran.dategn then rent.fuel else tran.fuel end fuel
from ...
A case can only return a single value per row (as it is called per row).

Error code ORA-06550 - Trouble concatenating strings in Oracle

I am making a table that contains the earnings for each month.
To do that I am using a for loop with an insert statement inside.
What I am having trouble with is converting the number into a month then into a char.
This is what my code looks like:
BEGIN
FOR i IN 1..12
LOOP
INSERT INTO REVENUE ( TO_CHAR(TO_DATE(i, 'MM'), 'MON') || '2009'
, select sum(transaction_amount)
but when I run this I get an error saying:
INSERT INTO REVENUE ( TO_CHAR(TO_DATE(i, 'MM'), 'MON') || '2009'
*
ERROR at line 4:
ORA-06550: line 4, column 31:
PL/SQL: ORA-00917: missing comma
What am I doing wrong here?
go-oleg is right, it isn't the concatenation that's the problem, it's that your syntax for the insert statement is wrong. You are missing the values keyword from the values clause:
BEGIN
FOR i IN 1..12
LOOP
INSERT INTO REVENUE VALUES ( TO_CHAR(TO_DATE(i, 'MM'), 'MON') || '2009'
, select sum(transaction_amount)
...
or ideally specify the column names you're inserting into:
BEGIN
FOR i IN 1..12
LOOP
INSERT INTO REVENUE ( <column1>, <column2> )
VALUES ( TO_CHAR(TO_DATE(i, 'MM'), 'MON') || '2009'
, select sum(transaction_amount)
...
Because you don't have the values keyword the parser thinks that the parentheses are enclosing a column list, and it's therefore confused when it sees the next opening bracket from the to_char - the error is against the bracket, which is char 31 if it starts with ma tab, which would also poibably explain why the asterisk marking the error position appears in slightly the wrong place. It's expecting a comma there as a delimiter in the column list. It apparently hasn't got as far as ecaluating whether 'to_char' is a valid column name.
Actually the select you're using for the second value suggests you might be trying to,use the subquery version; depending on what else you're doing in the rest of that statement, you might want:
BEGIN
FOR i IN 1..12
LOOP
INSERT INTO REVENUE ( <column1>, <column2> )
SELECT TO_CHAR(TO_DATE(i, 'MM'), 'MON') || '2009'
, sum(transaction_amount)
FROM <some other table>
...
I suspect you could probably do that with a single insertnrather than a loop, but hard to be sure without seeing the whole thing.

Oracle lag function, can it accept a column alias?

I am trying to use the lag function so I can compare one column to the last without using a cursor.
However the column I need to compare against has to go by an alias as I am using 3 unions).
Here is an example of what I am up to.
SELECT
'Y' AS paid,
lag(paid,1) over (ORDER BY salary) AS prev_paid
FROM pay
UNION
SELECT
'N' as paid,
lag(paid,1) over (ORDER BY salary) AS prev_paid
FROM not_paid
I keep getting the Error: PL/SQL: ORA-00904: "paid": invalid identifier
I suspect you want something more like this:
SELECT paid, lag(paid,1) over (ORDER BY salary) AS prev_paid
FROM
(
SELECT 'Y' as paid, salary
FROM pay
UNION
SELECT 'N' as paid, salary
FROM not_paid
)
The general answer is no: in Oracle you can never use a column alias at the level where it is defined, except in order by clauses.
However, your query has other issues, since you're getting the lag value of a constant. #Tony Andrew's query seems like what you actually want.

Resources