TRIGGER does not fire for deleting, but fire for inserting and updating - plsql

This is my first time creating Trigger. I found some similar questions, but I still cannot solve my error from those suggestions so I will ask here. Here are my tables:
Create Table Bill
(
Bill_Number Number(6,0) primary key,
Paid_YN Char(1),
Posted_YN Char(1)
);
And this table:
Create Table Bill_Item
(
Bill_Number Number(6,0) References Bill (Bill_Number),
Menu_Item_Number Number(5,0),
Quantity_Sold Number(3,0),
Selling_Price Number(6,2)
);
I need to create a trigger to prevent any insert, update, or delete on Bill_Item table if Paid_YN or/and Posted_YN is 'Y'. The messages must show which event and what reason (paid, posted or both).
And this is my code. Trigger works well with inserting, and updating but does not fire with deleting.
CREATE OR REPLACE TRIGGER TR_NO_POST
BEFORE INSERT OR UPDATE OR DELETE ON Bill_Item
FOR EACH ROW
BEGIN
SELECT Paid_YN, Posted_YN
INTO V_Paid_YN, V_Posted_YN
FROM Bill
WHERE Bill_Number = :NEW.Bill_Number;
IF inserting THEN
IF V_Paid_YN = 'Y' AND V_Posted_YN = 'N' THEN
RAISE_APPLICATION_ERROR(-20001, 'Bill has been paid. Cannot add more items!');
ELSIF V_Posted_YN = 'Y' AND V_Paid_YN = 'N'THEN
RAISE_APPLICATION_ERROR(-20002, 'Bill has been posted. Cannot add more items!');
ELSIF V_Paid_YN = 'Y' AND V_Posted_YN = 'Y' THEN
RAISE_APPLICATION_ERROR(-20003, 'Bill has been paid and posted. Cannot add more items!');
END IF;
ELSIF updating THEN
IF V_Paid_YN = 'Y' AND V_Posted_YN = 'N' THEN
RAISE_APPLICATION_ERROR(-20011, 'Bill has been paid. Cannot change!');
ELSIF V_Posted_YN = 'Y' AND V_Paid_YN = 'N'THEN
RAISE_APPLICATION_ERROR(-20022, 'Bill has been posted. Cannot change!');
ELSIF V_Paid_YN = 'Y' AND V_Posted_YN = 'Y' THEN
RAISE_APPLICATION_ERROR(-20033, 'Bill has been paid and posted. Cannot change!');
END IF;
ELSIF deleting THEN
IF V_Paid_YN = 'Y' AND V_Posted_YN = 'N' THEN
RAISE_APPLICATION_ERROR(-20111, 'Bill has been paid. Cannot delete!');
ELSIF V_Posted_YN = 'Y' AND V_Paid_YN = 'N'THEN
RAISE_APPLICATION_ERROR(-20222, 'Bill has been posted. Cannot delete!');
ELSIF V_Paid_YN = 'Y' AND V_Posted_YN = 'Y' THEN
RAISE_APPLICATION_ERROR(-20333, 'Bill has been paid and posted. Cannot delete!');
END IF;
END IF;
END TR_NO_POST;

You have to select from "Bill table" to check Paid_YN column value.
Something like this:
CREATE OR REPLACE TRIGGER TR_NO_POST
BEFORE INSERT OR UPDATE OR DELETE ON Bill_Item
FOR EACH ROW
paid_res Char(1);
posted_res Char(1);
BEGIN
select max(h.paid_yn),
max(h.posted_yn)
into paid_res, posted_res
from Bill h
where h.Bill_Number = :NEW.Bill_Number;
if paid_res = 'Y' then
RAISE_APPLICATION_ERROR(-20001, 'Bill has been paid!');
elsif posted_res = 'Y' then
RAISE_APPLICATION_ERROR(-20001, 'Bill has been posted!');
end if;
END TR_NO_POST;

Related

Assign variable error if 'no rows selected' from query

I want to assign a variable from the sale of today by the menu item input. If the menu item has been sold yet, the variable should return 0. But it showed error instead:
Error report -
SQL Error: ORA-00905: missing keyword
00905. 00000 - "missing keyword"
This is my code:
SELECT SUM(Selling_Price*Quantity_Sold)
INTO V_Today_Sale
FROM Bill_Item BI, Bill B
WHERE BI.Bill_Number = B.Bill_Number AND
Menu_Item_Number = 1 AND
Bill_Date = sysdate AND
(NVL(Paid_YN,'N') = 'Y' OR NVL(Posted_YN,'N') = 'Y')
GROUP BY Menu_Item_Number
ORDER BY Menu_Item_Number;
If I remove the variable assignment
SELECT SUM(Selling_Price*Quantity_Sold)
FROM Bill_Item BI, Bill B
WHERE BI.Bill_Number = B.Bill_Number AND
Menu_Item_Number = 1 AND
Bill_Date = sysdate AND
(NVL(Paid_YN,'N') = 'Y' OR NVL(Posted_YN,'N') = 'Y')
GROUP BY Menu_Item_Number
ORDER BY Menu_Item_Number;
the result will be:
no rows selected
It's correct since no sale has been made for today yet. But how can I make the query return 0 instead of no rows selected so that I can assign it to the variable?
Are you running the query as shown to test without putting it in a block (using PL/SQL or Toad)? If so, you need to define the variable and structure like this, with a call to NVL() around the SUM() call so V_Today_Sale gets a value if both inputs to SUM() are NULL, and add an exception to catch the NO_DATA_FOUND condition that sets V_Today_Sale to 0 if the where clause conditions are not met:
SET SERVEROUTPUT ON;
DECLARE
V_Today_Sale number := 0;
BEGIN
SELECT NVL(SUM(Selling_Price*Quantity_Sold), 0)
INTO V_Today_Sale
FROM Bill_Item BI, Bill B
WHERE BI.Bill_Number = B.Bill_Number AND
Menu_Item_Number = 1 AND
Bill_Date = sysdate AND
(NVL(Paid_YN,'N') = 'Y' OR NVL(Posted_YN,'N') = 'Y')
GROUP BY Menu_Item_Number
ORDER BY Menu_Item_Number;
EXCEPTION
WHEN NO_DATA_FOUND THEN
V_Today_Sale :=0;
END;
DBMS_OUTPUT.PUT_LINE(V_Today_Sale);

EXECUTE IMMEDIATE fail

I'm trying to use "EXECUTE IMMEDIATE", when I run the procedure I keep getting -
ORA-00904: "NOV": invalid identifier.
I checked the column like 20 times, the name is right. the table has only 2 columns - acc, run1.
also, in the output I get [using the dbms_output.put_line (v_stat)]:
update LAHAD2
set run1 = 19-NOV-17
am I missing something?
create or replace procedure lahad is
v_sec_t varchar2 (100) := 'LAHAD2';
v_date date := trunc (sysdate+10);
v_stat varchar2 (500);
begin
v_stat :=
'update '|| v_sec_t|| '
set run1 = ' || v_date ;
dbms_output.put_line (v_stat);
EXECUTE IMMEDIATE v_stat;
end Lahad;
You must have this:
update LAHAD2 set run1 = '19-NOV-17'
So for this above, you must have
v_stat := 'update '|| v_sec_t|| ' set run1 = ''' || v_date ||'''';
It might also be safer to have a to_date around this. To expect:
update LAHAD2 set run1 = to_date('19-NOV-17', 'DD-MON-YY')
You need
v_stat := 'update '|| v_sec_t|| ' set run1 = to_date(''' || v_date ||''') , ''DD-MON-YY'')';
Hope you get the thing, especially this '' to input single ' in the output.

Column exists but still get this error ORA-00904: invalid identifier

Anyone can help me with this issue
declare
lv2_sql VARCHAR2(32767);
cursor c_scv is
select financial_code, object_id, daytime from stream_category_version;
begin
for r_scv in c_scv LOOP
IF r_scv.financial_code = 'PURCHASE' THEN
lv2_sql := 'UPDATE stream_category_version ' || CHR(10) ||
'set REVN_PURCHASES_IND = ''Y'', last_updated_by = nvl(last_updated_by, created_by) ' || CHR(10) ||
'WHERE object_id = r_scv.object_id AND daytime = r_scv.daytime';
ecdp_dynsql.execute_statement(lv2_sql);
ELSIF r_scv.financial_code = 'SALE' THEN
lv2_sql := 'UPDATE stream_category_version ' || CHR(10) ||
'set REVN_SALES_IND = ''Y'', last_updated_by = nvl(last_updated_by, created_by) ' || CHR(10) ||
'WHERE object_id = r_scv.object_id AND daytime = r_scv.daytime';
ecdp_dynsql.execute_statement(lv2_sql);
END IF;
END LOOP;
end;
I have code as shown above, but i got error saying 'ORA-00904: R_SCV.DAYTIME: invalid identifier'. I have checked the table definition for 'stream_category_version' and found the column DAYTIME as shown below
SQL> desc stream_category_version
Name Type Nullable Default Comments
------------------ -------------- -------- ------- --------
OBJECT_ID VARCHAR2(32)
DAYTIME DATE
END_DATE DATE Y
NAME VARCHAR2(240) Y
FINANCIAL_CODE VARCHAR2(32) Y
SORT_ORDER NUMBER Y
COMMENTS VARCHAR2(2000) Y
Then i am confused with the error. Anyone can help me ?
Thanks in advance.
Shortly speaking - Oracle is case sensitive...
... probably during table creation column was typed UPPERCASE in quotation marks like that:
"DAYTIME"
and in your sql i see this column in lowercase
so you should verify your column name and best change it to version without quotation marks.
Other option is to call this column like that:
= r_scv.DAYTIME

getting total number to specific patients in rdlc report

I have made an rdlc report of total number of patients in a day and it works fine. I have the total number of female male patients, but when the report binds it returns a total equal to the number of rows in my data.
For example if I have 20 rows in my report then below when I print the count it returns the count in 20 rows.
How can I get it to be in only 1 row?
This is the query I'm using:
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON GO
ALTER PROCEDURE [dbo].[Test_test_test]-- '2013/08/02'
-- Add the parameters for the stored procedure here
-- Add the parameters for the stored procedure here
(#date VARCHAR(20))
AS
BEGIN
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT ( CASE
WHEN OLD_NEW_PATIENT.OLD_NEW = 'new' THEN
PATIENT_REF_MASTER.PAT_ID
ELSE NULL
END ) AS 'new',
( CASE
WHEN OLD_NEW_PATIENT.OLD_NEW = 'old' THEN
PATIENT_REF_MASTER.PAT_ID
ELSE NULL
END ) AS 'old',
----------------------------------------
( CASE
WHEN GENDER_MASTER.NAME1 = 'Female' THEN GENDER_MASTER.NAME1
ELSE NULL
END ) AS
'Females',
( CASE
WHEN GENDER_MASTER.NAME1 = 'Male' THEN GENDER_MASTER.NAME1
ELSE NULL
END ) AS 'Males',
-------------------------------------
CONVERT(VARCHAR, PATIENT_REF_MASTER.CREATION_DATE, 105) AS
'creation_Date',
PATIENT_REF_MASTER.SR_NO AS 'sr_No',
PATIENT_MASTER.PAT_FNAME + ' '
+ PATIENT_MASTER.PAT_SNAME AS 'NAME',
DEPT_ID AS
'Dept_ID',
DEPT_MASTER.DEPT_NAME AS
'Dept_Name',
DOC_MASTER.DOC_ID AS
'Doc_Master',
DOC_MASTER.DOC_FNAME + ' '
+ DOC_MASTER.DOC_SNAME AS
'Doc_Name'
,
PATIENT_MASTER.PAT_ADDR
AS 'addr',
GENDER_MASTER.NAME1 AS
'Pat_Sex',
PATIENT_MASTER.AGE AS 'age',
(SELECT Count(PATIENT_REF_MASTER.SR_NO)
FROM PATIENT_REF_MASTER
WHERE PATIENT_REF_MASTER.CREATION_DATE = #date) AS 'count',
(SELECT Count(PATIENT_MASTER.PAT_SEX)
FROM PATIENT_MASTER
LEFT JOIN PATIENT_REF_MASTER
ON PATIENT_REF_MASTER.PAT_ID =
PATIENT_MASTER.PAT_CODE
WHERE PATIENT_REF_MASTER.CREATION_DATE = #date
AND PATIENT_MASTER.PAT_SEX = 2) AS
'F_count',
(SELECT Count(PATIENT_MASTER.PAT_SEX)
FROM PATIENT_MASTER
LEFT JOIN PATIENT_REF_MASTER
ON PATIENT_REF_MASTER.PAT_ID =
PATIENT_MASTER.PAT_CODE
WHERE PATIENT_REF_MASTER.CREATION_DATE = #date
AND PATIENT_MASTER.PAT_SEX = 1) AS
'M_count'
FROM PATIENT_REF_MASTER
LEFT JOIN DBO.OLD_NEW_PATIENT
ON DBO.OLD_NEW_PATIENT.CODE = PATIENT_REF_MASTER.OLD_NEW
LEFT JOIN DBO.DEPT_MASTER
ON DEPT_MASTER.DEPT_CODE = PATIENT_REF_MASTER.DEPT_ID
LEFT JOIN PATIENT_MASTER
ON PATIENT_MASTER.PAT_CODE = PATIENT_REF_MASTER.PAT_ID
LEFT JOIN DOC_MASTER
ON DOC_MASTER.DOC_ID = PATIENT_REF_MASTER.DOC_ID
LEFT JOIN GENDER_MASTER
ON GENDER_MASTER.CODE = PATIENT_MASTER.PAT_SEX
WHERE PATIENT_REF_MASTER.CREATION_DATE = #date
--MONTH(Patient_Ref_master.creation_Date)=#month and Dept_ID=#dept
ORDER BY PATIENT_REF_MASTER.SR_NO ASC
-- select Dept_Master.Dept_Name as 'Dept_Name',
-- count(Pat_ID) as 'Pat_ID'
--
-- from Patient_Ref_master
--left join dbo.Dept_Master on Dept_Master.Dept_code = Patient_Ref_master.Dept_ID
--where MONTH(Patient_Ref_master.creation_Date)=#month and Dept_ID=#dept
--group by Dept_Master.Dept_Name
END
I think you're trying to do to many things at once ;-)
The heart of your problem lies here:
SELECT Count(PATIENT_MASTER.PAT_SEX)
FROM PATIENT_MASTER
LEFT JOIN PATIENT_REF_MASTER
ON PATIENT_REF_MASTER.PAT_ID =
PATIENT_MASTER.PAT_CODE
WHERE PATIENT_REF_MASTER.CREATION_DATE = #date
AND PATIENT_MASTER.PAT_SEX = 1
By using this query within your query, it will return the total in each and every row. This is also why you can get away with writing the query and not using the GROUP BY clause.
I suggest you do all the work in this query with the exception of the count, and then use another query outside of this one for the count.
A secondary problem is that in your query you're requesting several details about each row, but you want it to come back in one row. You have to decide what you want ;).
In order to get just the counts in one row, try something like this:
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON GO
ALTER PROCEDURE [dbo].[Test_test_test]-- '2013/08/02'
-- Add the parameters for the stored procedure here
-- Add the parameters for the stored procedure here
(#date VARCHAR(20))
AS
BEGIN
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT Count(*) count,
Sum(CASE
WHEN FEMALES IS NOT NULL THEN 1
ELSE 0
END) F_Count,
Sum(CASE
WHEN MALES IS NOT NULL THEN 1
ELSE 0
END) M_Count
FROM (SELECT ( CASE
WHEN OLD_NEW_PATIENT.OLD_NEW = 'new' THEN
PATIENT_REF_MASTER.PAT_ID
ELSE NULL
END ) AS
'new',
( CASE
WHEN OLD_NEW_PATIENT.OLD_NEW = 'old' THEN
PATIENT_REF_MASTER.PAT_ID
ELSE NULL
END ) AS
'old',
----------------------------------------
( CASE
WHEN GENDER_MASTER.NAME1 = 'Female' THEN
GENDER_MASTER.NAME1
ELSE NULL
END ) AS
'Females',
( CASE
WHEN GENDER_MASTER.NAME1 = 'Male' THEN
GENDER_MASTER.NAME1
ELSE NULL
END ) AS
'Males',
-------------------------------------
CONVERT(VARCHAR, PATIENT_REF_MASTER.CREATION_DATE, 105) AS
'creation_Date',
PATIENT_REF_MASTER.SR_NO AS
'sr_No',
PATIENT_MASTER.PAT_FNAME + ' '
+ PATIENT_MASTER.PAT_SNAME AS
'NAME'
,
DEPT_ID
AS 'Dept_ID',
DEPT_MASTER.DEPT_NAME AS
'Dept_Name',
DOC_MASTER.DOC_ID AS
'Doc_Master',
DOC_MASTER.DOC_FNAME + ' '
+ DOC_MASTER.DOC_SNAME AS
'Doc_Name',
PATIENT_MASTER.PAT_ADDR AS
'addr'
,
GENDER_MASTER.NAME1
AS 'Pat_Sex',
PATIENT_MASTER.AGE AS
'age'
FROM PATIENT_REF_MASTER
LEFT JOIN DBO.OLD_NEW_PATIENT
ON DBO.OLD_NEW_PATIENT.CODE =
PATIENT_REF_MASTER.OLD_NEW
LEFT JOIN DBO.DEPT_MASTER
ON DEPT_MASTER.DEPT_CODE =
PATIENT_REF_MASTER.DEPT_ID
LEFT JOIN PATIENT_MASTER
ON PATIENT_MASTER.PAT_CODE =
PATIENT_REF_MASTER.PAT_ID
LEFT JOIN DOC_MASTER
ON DOC_MASTER.DOC_ID = PATIENT_REF_MASTER.DOC_ID
LEFT JOIN GENDER_MASTER
ON GENDER_MASTER.CODE = PATIENT_MASTER.PAT_SEX
WHERE PATIENT_REF_MASTER.CREATION_DATE = #date
--MONTH(Patient_Ref_master.creation_Date)=#month and Dept_ID=#dept
)T
ORDER BY PATIENT_REF_MASTER.SR_NO ASC
END
I hope this helps, let me know if you need any more info.
Edit
Found a small mistake in the query I posted, the field names should have been FEMALES not FEMALE and MALES not MALE.
I also set up a scaled down example in SQL Fiddle. Take a look and tell me what you think.

PL/SQL: Conditional Where

I have the following scenario:
CREATE OR REPLACE PROCEDURE GETINBOX
(
inHasAttachments IN int
)
AS
BEGIN
SELECT M.MailId,
M.SenderId,
E.Emp_Name As "Sender",
MI.RecipientId,
M.Subject
FROM MAIL M INNER JOIN MAILINBOX MI ON M.MailId = MI.MailId
WHERE MI.RecipientId = '547' AND
M.NotificationSelected = 'Y'
IF inHasAttachments = '1' THEN
AND M.Attachments = 'Y'
END IF;
END GETINBOX;
Is it possible to add conditions to the where clause based on the value of a parameter?
WHERE MI.RecipientId = '547' AND
M.NotificationSelected = 'Y'
IF inHasAttachments = '1' THEN
AND M.Attachments = 'Y'
END IF;
Obviously this is not allowed but is it possible to do this in some way in PL/SQL?
I know one way to do it is to duplicate the query and execute a different query based on the value of the parameter but I don't want to duplicate my code.
As I understand your requirements: if the value of parameter inHasAttachments is 1 then you want to filter further by M.Attachments = 'Y', and if its value isn't 1 then you don't care about M.Attachments. This is in addition to the condition MI.RecipientId = '547' AND M.NotificationSelected = 'Y'.
You can do it like this:
SELECT M.MailId,
M.SenderId,
E.Emp_Name As "Sender",
MI.RecipientId,
M.Subject
FROM MAIL M INNER JOIN MAILINBOX MI ON M.MailId = MI.MailId
WHERE MI.RecipientId = '547' AND M.NotificationSelected = 'Y'
AND (inHasAttachments <> '1' OR M.Attachments = 'Y')

Resources