How to use checkbox in apex5 and when checked it update table - oracle-apex-5

I'm new to apex and have a region (interactive report) tht contains students details One of the field is date_of_entry I would like to add a checkbox for this field and when user check it. Then tht specific row get automatically updated in table with current sysdate.
How to do that add wz examples pls

Interactive Reports are not editable in versions < 5.1. So, you will need to add an editable column using APEX_ITEM function to display checkbox item on every row.
SELECT A.COURSE_ID,
A.COURSE_NAME,
B.PAYMENT_DATE,
B.RECEIVEDBY_EMPL,
APEX_ITEM.CHECKBOX2(1,A.COURSE_ID) "Select"
FROM COURSE_INFO A,
PAYMENT_INFO B
WHERE A.COURSE_ID = B.COURSE_ID
AND A.STUDENT_ID = :P2_ID;
Link for more info on this function.
Now processing part, all APEX items are posted on form submit regardless of whether user has checked them or not. Loop through all items and update table where value is not null.
DECLARE
L_COURSE_ID WWV_FLOW_GLOBAL.VC_ARR2;
BEGIN
L_COURSE_ID := APEX_APPLICATION.G_F01;
FOR IDX IN 1 .. L_COURSE_ID.COUNT
LOOP
IF L_COURSE_ID(IDX) IS NOT NULL THEN
UPDATE PAYMENT_INFO
SET PAYMENT_DATE=SYSDATE
WHERE COURSE_ID =L_COURSE_ID(IDX)
AND STUDENT_ID =:P2_ID;
END IF;
END LOOP;
END;

Related

Create an instance of a table in a single row?

I'm having trouble figuring out how to create a snapshot (or instance) of a table when the user updates the table in the UI.
The problem is:
I have a UI for "Amendments." In the UI, there is a dropdown list for "Amendment Type." Amendment Type also has its own UI, and the user can check/un-check the list of available Amendment Types here. When the user un-checks an Amendment Type, that type gets removed from the dropdown list for all Amendments. The client asks that changes to the Amendment Type ONLY affect NEW Amendments, and not ones that existed before the change.
So, if the Amendment Type values for "New, Draft, Closed" were checked previously, and "Draft" was un-checked, I would still need to display all three values in the UI for existing Amendments, and then only display "New" and "Closed" for new Amendments. But then, they can go back in and reopen the "Draft" and have that display once again, but only for Amendments created after they reopened it.
To me, this means that I need to create a table for "Amendment Type History." The table for "Amendment Type" has an AmendmentTypeID column where all of the IDs are displayed as rows. I would display those as columns, with the row defined by the Effective Date--the date that the values were changed. Then I would link the Amendment to the AmendmentTypeHistoryID and get the values to display by looking up the AmendmentTypeIDs.
e.g.
dbo.AmendmentType
AmendmentTypeID Name CreationDate IsActive
1 New 6/2/2019 1
2 Draft 6/2/2019 1
3 Closed 6/2/2019 1
dbo.AmendmentTypeHistory
AmendmentTypeHistoryID EffectiveDate AmendmentTypeID AmendmentTypeID AmendmentTypeID
1 6/3/2019 1 (New) 2 (Draft) 3 (Closed)
Then you change it...
dbo.AmendmentType
AmendmentTypeID Name CreationDate IsActive
1 New 6/2/2019 1
3 Closed 6/2/2019 1
dbo.AmendmentTypeHistory
AmendmentTypeHistoryID EffectiveDate AmendmentTypeID AmendmentTypeID AmendmentTypeID
1 6/3/2019 1 (New) 2 (Draft) 3 (Closed)
2 6/3/2019 1 (New) 3 (Closed) NULL
There are 77 Amendment Type possibilities total. It's not something I can hard code, so I was hoping I could do it dynamically somehow.
Does anyone know how I could do this? Is there an easier or better way to do what I'm trying to do?
This is a very difficult and confusing requirement. What happens if the Amendment Types which IsActive are different between the moment you load them into the drop down and the moment the user saves their entry?
Ignoring this, I would recommend a table which would map combinations in an ordered comma-seperated list. It is very rare that my answer breaks Normal Forms, but it would be difficult otherwise.
Make a table called AmendmentTypeGroup:
create table AmendmentTypeGroup
(
AmendmentTypeGroupID int identity
,AmendmentTypes nvarchar(max) not null
,constraint PK_AmendmentTypeGroup primary key clustered(AmendmentTypeGroupID)
)
Add a column to your Amendments (not amendments type) table:
alter table Amendmends add AmendmentTypeGroupID int null
Each time you insert a row in Amendments, use a trigger or proc to ensure you first insert into the AmendmentTypeGroup a group of all the IDs of all AmendmentTypes who are IsActive at that moment, get the group ID, and then insert the Amendments row with the group value you inserted. If there were already a group with the specific combination, just grab its id and use that instead.
declare #amendmentCombination nvarchar(max)
declare #groupID int
select amendmentCombination=string_agg(convert(nvarchar(max),AmendmentTypeID),',') WITHIN GROUP ( ORDER BY AmendmentTypeID asc)
from AmendmentType
where IsActive = 1 -- will only work for sql server version 2017. Search XML path concatenation if you don't have it
select #groupID=AmendmentTypeGroupID
from AmendmentTypeGroup
where AmendmentTypes=#amendmentCombination
if #groupID is null
begin
insert AmendmentTypeGroup(AmendmentTypes) select #amendmentCombination
set #groupID=SCOPE_IDENTITY() -- last id entered
end
insert Amendments(..your_other_columns...,AmendmentTypeGroup)
select ..your_other_columns...,#groupID
This way, for each Amendment row, you can join it with AmendmentTypeGroup to get the comma seperated list of available AmendmentTypes at that time. You will have to split it to take in in a tabular form.
To make your data more robust, you ought to 1) Manually create all AmendmentTypeGroups which were used for already existing rows in amendments, and insert their IDs in Amendment.AmendmentTypeGroup, so that you can then 2) create a FK on Amendment.AmendmentTypeGroup referenceing AmendmentTypeGroup.AmendmentTypeGroupID 3) create a unique constraint on AmendmentTypeGroup.AmendmentTypes
As you can see, this is difficult. Tread carefully.
After you comments I think I have a solution for you.
Create a table AmendmentTypeVersions like this
ID | VERSION_NUMBER | AmendmentTypeId
and insert all active AmendmentTypes using version 1
INSERT INTO AmendmentTypeVersions (VERSION_NUMBER , AmendmentTypeId)
SELECT 1, AmendmentTypeID
FROM AmendmentType
WHERE isactive = 1
Every time you have save the AmendmentType, get the max version number and insert the records of the new version
DECLARE #LastVersion = (SELECT MAX(VERSION_NUMBER) FROM AmendmentTypeVersions)
INSERT INTO AmendmentTypeVersions (VERSION_NUMBER , AmendmentTypeId)
SELECT #LastVersion + 1, AmendmentTypeID
FROM AmendmentType
WHERE isactive = 1
Add to column to Amendments with the VERSION_NUMBER and set it to 1
Now all you have to do is this:
When you are creating an Amendment you need to get then set the Amendment VERSION_NUMBER to the #lastVersion (On the edit operation you already have Amendment VERSION_NUMBER set)
always filter the AmendmentTypes by the VERSION_NUMBER of the Amendment (you have to join amendmentTypes with the AmendmentTypeVersions by the VERSION_NUMBER of the Amendment)

Passing Multiple Values into an API/Getting Multiple Values

Here's the issue that I'm working through today. We have an ERP system in place and I need to set up an event to email Purchase Order Authorizers (POA) whenever they have a Purchase Order ready for approval. This is pretty easy to set up for purchase orders where we have a single authorizer. However, some purchase orders need to be reviewed by a group of authorizers, specifically our QA group, before they can get approved.
I can get a list of authorizers for our QA group using the following:
select authorize_id from purch_authorize_group_line where authorize_group_id = '30-QA-NUC';
This query returns 50 rows of data.
I can also get the USERID associated with an Authorize ID using the following:
SELECT purchase_authorizer_api.get_userid('30','104351') FROM DUAL;
What I can't figure out is how to pass all values from the first query into the second one. This query:
SELECT purchase_authorizer_api.get_userid('30',(select authorize_id from purch_authorize_group_line where authorize_group_id = '30-QA-NUC')) FROM DUAL;
returns the error "ORA-01427: single-row subquery returns more than one row."
So, what I'm wondering, is whether there's a way for me to pass all 50 values from the first query into the second query and get USERIDs for all 50 users. If those USERIDs then I can get a notification email out to the QA group when they have a PO ready to approve.
Maybe you're looking for this.
SELECT purchase_authorizer_api.get_userid('30', authorize_id)
FROM purch_authorize_group_line
WHERE authorize_group_id = '30-QA-NUC';
This should help -
DECLARE
V_VARIABLE VARCHAR2(1024);
BEGIN
FOR REC IN (SELECT AUTHORIZE_ID AS VAL FROM PURCH_AUTHORIZE_GROUP_LINE WHERE AUTHORIZE_GROUP_ID = '30-QA-NUC')
LOOP
SELECT PURCHASE_AUTHORIZER_API.GET_USERID('30',R.VAL) INTO V_VARIABLE FROM DUAL;
..
..
../* You code processing logic */
END LOOP;
END;
/

How to do something on insert when condition

I have two tables, one with a list of movies, and one with a list of dates when each movie is played. The movies list has the columns name, id, start_date and end_date, while id is a unique identifier.
The shows list (the one with the dates) has id,movie_id,date.
Every time I INSERT a new show, I'd like the movies list to be updated: If the show.date is before the movie.start_date, I'd like the start_date to be updated to the value of the show.date. Same goes for the end date - obviously if the show.date is after the movie.end_date.
The following rule is what I am stuck with: (NOTE: It would only set the start date if it worked, getting the end date done should be easy once this works...)
CREATE RULE "movies_start_date_setter" AS ON INSERT TO "shows"
WHERE movies.id = NEW.movie_id AND movies.start_date < NEW.date
DO (UPDATE movies SET start_date = NEW.date);
It returns: ERROR: missing FROM-clause entry for table "movies"
This error doesn't give me (as a beginner) any information where I'm missing a FROM clause and why.
Now since everybody seems to think a rule is a bad idea (and I tend to bow the pressure) I tried a trigger, which is absolutely totally new to me.
Using a trigger (highly recommended) it would look something like this:
CREATE OR REPLACE FUNCTION adjust_start_date()
RETURNS trigger AS
$BODY$
BEGIN
UPDATE movies SET start_date = NEW.date
WHERE id = NEW.movie_id AND start_date > NEW.date;
RETURN NEW;
END;
$BODY$
LANGUAGE 'plpgsql';
Followed by:
CREATE TRIGGER adjust_start_date_trigger
AFTER INSERT
ON shows
FOR EACH ROW
EXECUTE PROCEDURE adjust_start_date();
This trigger may or may not work, it solves my issue but doesn't answer my original question...
Anybody else out there smarter than me (I sure hope so!)?
The table movies is not known in where clause. Use:
create rule movies_start_date_setter as
on insert to shows do (
update movies
set start_date = new.date
where id = new.movie_id and start_date > new.date
);
The reson for the error message is that you reference table movies in the WHERE clause.
You could try with a query rewrite rule like this:
CREATE RULE movies_start_date_setter AS
ON INSERT TO shows DO ALSO
UPDATE movies
SET start_date = LEAST(NEW.date, start_date),
end_date = GREATEST(NEW.date, end_date)
WHERE id = NEW.movie_id
AND NEW.date NOT BETWEEN start_date AND end_date;
but that won't work with multi-line INSERTs like this:
INSERT INTO shows (id, movie_id, date) VALUES
(1, 42, <very small date>),
(2, 42, <very large date>);
because only one of the dates in the movies row with id = 42 will get changed.
Rules are tricky and hard to understand.
You are better off with a trigger.

IF statement in WHERE clause with Oracle SQL on count

I am trying to develop a PUT method for a website. I am using the following code to make sure the information the user is entering is different from what we already have before making changes to the database (also to prevent a whole bunch of log files in case the user hits submit too many times):
SELECT COUNT(*) AS count
FROM iam.credential
WHERE iam.credential.CREDENTIAL_TYPE = :1
AND iam.credential.CREDENTIAL_NAME = :2
AND iam.credential.LOST_OR_STOLEN = :3
AND iam.credential.STATUS = :4
AND iam.credential.EXPIRATION_DATE = :5
AND iam.credential.ISSUING_LOCATION = :6
AND iam.credential.PHYSICAL_FORM = :7
AND iam.credential.ASSOCIATED_DEVICE = :8
AND iam.credential.DISPLAY_NAME = :9;
I am grabbing the values from my webpage, but I am running into issues when the value is NULL. I want to be able to do something like the following:
SELECT COUNT(*) AS count
FROM iam.credential
WHERE
IF iam.credential.CREDENTIAL_TYPE is not null THEN
iam.credential.CREDENTIAL_TYPE = :1
ELSE
iam.credential.CREDENTIAL_TYPE is null
END IF
AND
IF iam.credential.CREDENTIAL_NAME is not null THEN
iam.credential.CREDENTIAL_NAME = :2
ELSE
iam.credential.CREDENTIAL_NAME is null
END IF
//and so on
I can't use
SELECT COUNT(*) AS count
FROM iam.credential
WHERE (iam.credential.CREDENTIAL_TYPE = :1
OR iam.credential.CREDENTIAL_TYPE is null)
because that will return a count of 2 when I only want the one that matches what the user input.
Basically I want the count to either return a 1 or a 0 for if the record exists or if it doesn't.
I want the WHERE clause to be dynamically changed based on what the user provides.
If the user doesn't provide a value because it is not required it will be null. I need query to change to
credential_name is null
because
credential_name = null
doesn't work in oracle.
if it is not null then I need it to be
credential_name = :1
and it will be filled with the value that the user provided.
credential_name is :1
doesn't work in oracle either.
I will get two records back if I have two credentials of the same type but one record has a (null) value for CREDENTIAL_NAME and the other has 'DaisyDuck'.
You may just want to check that both the column value and the supplied value are null, or the column and the supplied value match:
SELECT COUNT(*) AS count
FROM iam.credential
WHERE (iam.credential.CREDENTIAL_TYPE = :1
OR (iam.credential.CREDENTIAL_TYPE is null AND :1 is null))
AND (iam.credential.CREDENTIAL_NAME = :2
OR (iam.credential.CREDENTIAL_NAME is null AND :2 is null))
AND ...
You may not have to do that for every value if you have table columns that are not nullable and you're checking that the matching user-supplied value is not null before you reach this point.
Your approach has a potential flaw though. In a multi-user system two sessions could enter the same values, both check and get a count of zero, and both then insert - causing a duplicate row unless you also have a unique constraint across all the values. If you do have such a constraint then the count will prevent a violation on insert some of the time, but not always, so may not be worth the extra trip to the database.

Getting original and modified content from a table with an audit trail

I came across the following table structure and I need to perform a certain type of query upon it.
id
first_name
last_name
address
email
audit_parent_id
audit_entry_type
audit_change_date
The last three fields are for the audit trail. There is a convention that says: all original entries have the value "0" for "audit_parent_id" and the value "master" for "audit_entry_type". All the modified entries have the value of their parent id for audit_parent_id" and the value "modified" for the "audit_entry_type".
Now what I want to do is to be able to get the original value and the modified value for a field and I want to make this with less queries possible.
Any ideas? Thank you.
Assuming a simple case, when you want to get the latest adress value change for the record with id 50, this query fits your needs.
select
p.id,
p.adress as original_address,
(select p1.adress from persons p1 where p1.audit_parent_id = p.id order by audit_change_date desc limit 1) as latest_address
from
persons p -- Assuming it's the table name
where
p.id = 50
But this assumes that, even if the address value doesn't change between one audit to the other, it remains the same in the field.
Here's another example, showing all persons that had an address change:
select
p.id,
p.adress as original_address,
(select p1.adress from persons p1 where p1.audit_parent_id = p.id order by audit_change_date desc limit 1) as latest_address
from
persons p -- Assuming it's the table name
where
p.audit_parent_id = 0
and
p.adress not like (select p1.adress from persons p1 where p1.audit_parent_id = p.id order by audit_change_date desc limit 1)
This can be solved with pure SQL in modern Postgres using WITH RECURSIVE.
For PostgreSQL 8.3, this plpgsql function does the job while it is also a decent solution for modern PostgreSQL. You want to ..
get the original value and the modified value for a field
The demo picks first_name as filed:
CREATE OR REPLACE FUNCTION f_get_org_val(integer
, OUT first_name_curr text
, OUT first_name_org text) AS
$func$
DECLARE
_parent_id int;
BEGIN
SELECT INTO first_name_curr, first_name_org, _parent_id
first_name, first_name, audit_parent_id
FROM tbl
WHERE id = $1;
WHILE _parent_id <> 0
LOOP
SELECT INTO first_name_org, _parent_id
first_name, audit_parent_id
FROM tbl
WHERE id = _parent_id;
END LOOP;
END
$func$ LANGUAGE plpgsql;
COMMENT ON FUNCTION f_get_org_val(int) IS 'Get current and original values for id.
$1 .. id';
Call:
SELECT * FROM f_get_org_val(123);
This assumes that all trees have a root node with parent_id = 0. No circular references, or you will end up with an endless loop. You might want to add a counter and exit the loop after x iterations.