In my Apex Application there is the opportunity to set filters for creating a chart. A user can choose several systems if he wants to. The systems are created as a checkbox item. The source of the checkbox item is based on a list of values which contains a SQL Query to retrieve all systems out of my database table. Here is the structure of my database table:
CREATE TABLE system_table (
system_id NUMBER(16) NOT NULL,
system_name VARCHAR2(100 CHAR) NOT NULL,
system_table_uebergeordnet_ID NUMBER(16),
CONSTRAINT system_name_unique UNIQUE(system_name),
CONSTRAINT system_table_pk PRIMARY KEY ( system_id ),
CONSTRAINT system_table_uber_id_fk FOREIGN KEY ( system_table_uebergeordnet_ID )
REFERENCES system_table ( system_id ) );
As you can see in the code, the last attribute is a recursive relationship. What I want to have now is the following: If a user selects a system_name and submits the page, the system name and all other systems that are referencing the chosen system should be selected. Therefore I have created a hidden item called System_ID. Before submitting the page I have defined a dynamic action which picks up the id of the chosen system_name. After submitting the page, my chart is created and I am checking for the condition in a where-clause. It looks like that:
where ((instr(':' || upper(:P26_SYSTEMS) || ':', upper(SYSTEM_TABLE.SYSTEM_NAME)) > 0) or (instr(':' || upper(:P26_SYSTEMS_ALL) || ':', upper(SYSTEM_TABLE.SYSTEM_NAME)) > 0) or (SYSTEM_TABLE.SYSTEM_TABLE_UEBERGEORDNET_ID = :P26_SYSTEM_ID))
The query is working so far, but only selects the chosen system_name and not the systems who are referencing the chosen system. I hope I was able to explain the problem and you can understand it. Does anyone of you know what I am doing wrong here?
The following code shows my query:
select COUNT(TRIGGER_TABLE.DATUM_UHRZEIT) as Anzahl_Trigger,
TEST.NUMMER as NUMMER
from BRIDGE_SYSTEM_TRIGGER, SYSTEM_TABLE, TRIGGER_TABLE, FAHRT, TEST, MITARBEITER
where BRIDGE_SYSTEM_TRIGGER.SYSTEM_TABLE_SYSTEM_ID = SYSTEM_TABLE.SYSTEM_ID
and BRIDGE_SYSTEM_TRIGGER.TRIGGER_TABLE_TRIGGER_ID = TRIGGER_TABLE.TRIGGER_ID
and TRIGGER_TABLE.FAHRT_FAHRT_ID = FAHRT.FAHRT_ID
and MITARBEITER.NUMMER = FAHRT.MITARBEITER_NUMMER
and FAHRT.TEST_ID= TEST_ID
and TRIGGER_TABLE.PRIORITAET = 1
and ((instr(':' || upper(:P26_TEST) || ':', upper(TEST.TEST_ID)) > 0) or (instr(':' || upper(:P26_TEST_ALL) || ':', upper(TEST.TEST_ID)) > 0))
and ((instr(':' || upper(:P26_SYSTEMS) || ':',':' || upper(system_table.system_name) ||':') > 0) or (instr(':' || upper(:P26_SYSTEMS_ALL) || ':', upper(SYSTEM_TABLE.SYSTEM_NAME)) > 0) or exists (select child.system_id from system_table child where instr(':' || upper(:P26_SYSTEMS) ||':',':'|| upper(child.system_name) ||':') > 0 and child.system_table_uebergeordnet_id = system_table.system_id))
and ((instr(':' || upper(:P26_COUNTRIES) || ':', upper(FAHRT.LAND)) > 0) or (instr(':' || upper(:P26_COUNTRIES_ALL) || ':', upper(FAHRT.LAND)) > 0))
and ((instr(':' || upper(:P26_FAHRER) || ':', upper(MITARBEITER.QNUMMER)) > 0) or (instr(':' || upper(:P26_FAHRER_ALL) || ':', upper(MITARBEITER.QNUMMER)) > 0))
GROUP BY TEST.NUMMMER
ORDER BY TEST.NUMMER;
The query counts the number of triggers per priority ordered by Testnumbers. The triggers that are meant here have nothing to do with sql triggers! So please don't be confused about that term.
A 'Fahrt' belongs to one 'Test' and a 'Test' can contain several 'Fahrten'. Furthermore each 'Fahrt' contains several triggers. The last four where conditions are the mentioned conditions to retrieve the filterinformation that have been set and only those triggers that met the filter conditions are counted.
A hierarchical data structure can be queried using a hierarchical query. In Oracle you can use something like this (note: not tested):
select ...
from system_table
where instr(':'||upper(:P26_SYSTEMS)||':'
,':'||upper(system_table.system_name)||':') > 0
or system_table.system_id in (
select parent.system_id
from system_table parent
start with instr(':'||upper(:P26_SYSTEMS)||':'
,':'||upper(parent.system_name)||':') > 0
connect by prior parent.system_table_uebergeordnet_id = parent.system_id
)
This hierarchical query starts from the selected system name(s) and walks up the hierarchy to their parents, grandparents, etc. to the top. The resulting set of system IDs is then used to filter the main table.
If you need it to select any children of a selected system, switch the connect by clause, e.g.
select ...
from system_table
where instr(':'||upper(:P26_SYSTEMS)||':'
,':'||upper(system_table.system_name)||':') > 0
or system_table.system_id in (
select parent.system_id
from system_table parent
start with instr(':'||upper(:P26_SYSTEMS)||':'
,':'||upper(parent.system_name)||':') > 0
connect by prior parent.system_id = parent.system_table_uebergeordnet_id
)
On the third hand, if it's not a multi-level hierarchy you can make the query simpler, e.g.
select ...
from system_table
where instr(':'||upper(:P26_SYSTEMS)||':'
,':'||upper(system_table.system_name)||':') > 0
or exists (
select child.system_id
from system_table child
where instr(':'||upper(:P26_SYSTEMS)||':'
,':'||upper(parent.system_name)||':') > 0
and child.system_table_uebergeordnet_id = system_table.system_id
)
Related
I have a report outputting the results of a query which was designed to provide links to a webpage:
SELECT
a,
b,
c,
'string' ||
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%URL_LINK~' || ipp_code || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY
) AS URL
FROM men_ipp
This works well, but I was asked to amend it so that if the records needed to generate the URL were missing (ie. sso_code can't be retrieved), it outputs a warning message instead of the subquery output.
Since there's always going to be a string of a set length (6 characters in this example), my solution was to create a CASE statement which is evaluating the length of the subquery output, and if the answer is greater than 6 characters it returns subquery result itself, otherwise it returns a warning message to the user. This looks like:
SELECT
a,
b,
c,
CASE
WHEN
LENGTH('string' ||
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%IPP_URL_LINK~' || (ipp_code) || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY)
) > 6
THEN
('string' ||
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%IPP_URL_LINK~' || (ipp_code) || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY)
ELSE 'warning message'
END AS URL
FROM men_ipp
The statment works fine, however the processing time is nearly doubled because it's having to process the subquery twice. I want to know if there's any way to store the result of the subquery in the WHEN, so it doesn't need to be run a second time in the THEN? eg. as a temporary variable or similar?
I've tried to declare a variable like this:
DECLARE URLLINK NVARCHAR(124);
SET URLLINK = 'string' ||
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%URL_LINK~' || ipp_code || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY
)
However this causes the query to error saying the it Encountered the symbol "https://evision.dev.uwl.tribalsits.com/urd/sits.urd/run/siw_file_load.sso?" when expecting one of the following: := . ( # % ; not null range default character
You can use NULLIF to make the result null if it is "string" (i.e., you appended nothing to it from your subquery). Then use NVL to convert to the warning message. Something like this:
SELECT
a,
b,
c,
nvl(nullif(
'string' ||
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%IPP_URL_LINK~' || (ipp_code) || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY),'string'),'warning message')
FROM men_ipp
Use a CTE.
with temp as
(SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%IPP_URL_LINK~' || (ipp_code) || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY
)
select a, b, c,
case when sso_code is null then 'warning message'
else 'string' || sso_code
end as url
from men_ipp full outer join temp on 1 = 1;
Use a sub-query:
SELECT a,
b,
c,
CASE
WHEN LENGTH(sso_code) > 6
THEN sso_code
ELSE 'warning message'
END AS URL
FROM (
SELECT a,
b,
c,
'string' ||
( SELECT sso_code
FROM men_sso
WHERE sso_parm LIKE '%IPP_URL_LINK~' || ipp_code || '%'
ORDER BY sso_cred, sso_cret
FETCH FIRST 1 ROWS ONLY ) AS sso_code
FROM men_ipp
)
SELECT DISTINCT 'update loc_final_2 set county = ''' || TRIM(upper(county) ) || '''
where city = '''
|| TRIM(upper(city) ) || ''' and state = ''' || TRIM(upper(state) ) || ''';'
FROM audtr_rgn_assgnmnts;
commit
can somebody tell me what the above query does. There's a table called loc_final_2 and another one called audtr_rgn_assgnmnts. I don't understand what the query is doing and I need to improve this query. But before that I have to understand what it is doing. I also need to know what commit is doing at the end.
The query is trying to generate multiple update statements for the table loc_final_2 using the values from audtr_rgn_assgnmnts.
I'm not sure why the commit exists there as the queries are not being executed.
If at all the intention of the original coder of the query was to run multiple updates, then I would say it's a wrong approach. A single update / MERGE like this would be more straightforward and performant.
UPDATE loc_final_2 l
SET
county = (
SELECT upper(TRIM(county) ) --Add MIN/MAX if there are multiple rows
FROM audtr_rgn_assgnmnts a
WHERE l.city = upper(TRIM(a.city) ) AND l.state = upper(TRIM(a.state) )
);
MERGE INTO loc_final_2 t USING audtr_rgn_assgnmnts s ON (
l.city = upper(TRIM(a.city) ) AND l.state = upper(TRIM(a.state) )
)
WHEN MATCHED THEN UPDATE SET t.county = UPPER(TRIM(s.county));
If there are multiple rows, you would need to do a select distinct in the USING
MERGE INTO loc_final_2 t USING (
SELECT DISTINCT city,
state,
upper(TRIM(s.county) ) AS county
FROM audtr_rgn_assgnmnts
)
s ON (
l.city = upper(TRIM(a.city) ) AND l.state = upper(TRIM(a.state) )
)
WHEN MATCHED THEN UPDATE SET t.county = s.county;
EDIT :
what is the 3-single quote doing in the query
A literal quote in Oracle needs to be escaped using another single quote.
So, If your final query needs to have something like a='b' , the expression generating that string should be a=||'''b||''' one the quite itself, one quote to escape that quote, other the part of concatenation.
because min/max can be used only for numbers, right?
MAX and MIN can be used on strings. Here I said you may use it to avoid duplicate values for same county returned from the inner query. DISTINCT is also an option if you don't want to use MIN/MAX.
I got these three giant schema in Oracle which I call them db layers (L3, L2, L1).
In each layer I got many SPs which might call some procedures from their underlying layers. Now for documentation purposes I need to draw something like a tree to show these chain calls. Well I'm not interested to get involved in the drudgery of extracting this data manually.
The question is, is there an automated way to do this? like a query to find out who calls who.
I was just fiddling around a little. So maybe like a starting point.
Replace DBA_OBJECTS.OWNER IN ('HUSQVIK') with your schemas.
WITH leafs AS (
SELECT
DBA_OBJECTS.OWNER, DBA_OBJECTS.OBJECT_NAME NAME,
CASE WHEN COUNT(PARENT_REFERENCES.REFERENCED_NAME) > 0 THEN 1 ELSE 0 END IS_REFERENCED,
CASE WHEN COUNT(CHILD_REFERENCES.NAME) > 0 THEN 1 ELSE 0 END HAS_REFERENCES
FROM
DBA_OBJECTS
LEFT JOIN DBA_DEPENDENCIES PARENT_REFERENCES ON DBA_OBJECTS.OWNER = PARENT_REFERENCES.REFERENCED_OWNER AND DBA_OBJECTS.OBJECT_NAME = PARENT_REFERENCES.REFERENCED_NAME
LEFT JOIN DBA_DEPENDENCIES CHILD_REFERENCES ON DBA_OBJECTS.OWNER = CHILD_REFERENCES.OWNER AND DBA_OBJECTS.OBJECT_NAME = CHILD_REFERENCES.NAME
WHERE
OBJECT_TYPE IN ('PACKAGE BODY', 'FUNCTION', 'PROCEDURE')
AND DBA_OBJECTS.OWNER IN ('HUSQVIK')
GROUP BY
DBA_OBJECTS.OWNER, DBA_OBJECTS.OBJECT_NAME
)
SELECT 'Entry point -> ' || OWNER || '.' || NAME DEPENDENCY_PATH, 1 MAX_STACK_DEPTH FROM leafs WHERE leafs.IS_REFERENCED = 0 AND leafs.HAS_REFERENCES = 0
UNION ALL
SELECT
DEPENDENCY_PATH, STACK_DEPTH
FROM (
SELECT
'Entry point -> ' ||
CONNECT_BY_ROOT DBA_DEPENDENCIES.OWNER || '.' || CONNECT_BY_ROOT DBA_DEPENDENCIES.NAME ||
SYS_CONNECT_BY_PATH(DBA_DEPENDENCIES.REFERENCED_OWNER || '.' || DBA_DEPENDENCIES.REFERENCED_NAME, ' -> ') DEPENDENCY_PATH,
CONNECT_BY_ISLEAF ISLEAF,
LEVEL + 1 STACK_DEPTH
FROM
DBA_DEPENDENCIES
LEFT JOIN
(SELECT * FROM leafs WHERE leafs.IS_REFERENCED = 0) roots
ON roots.OWNER = DBA_DEPENDENCIES.OWNER AND roots.NAME = DBA_DEPENDENCIES.NAME
WHERE
DBA_DEPENDENCIES.REFERENCED_TYPE IN ('PACKAGE BODY', 'FUNCTION', 'PROCEDURE')
START WITH
roots.NAME IS NOT NULL
CONNECT BY NOCYCLE
PRIOR DBA_DEPENDENCIES.REFERENCED_OWNER = DBA_DEPENDENCIES.OWNER AND
PRIOR DBA_DEPENDENCIES.REFERENCED_NAME = DBA_DEPENDENCIES.NAME)
WHERE ISLEAF = 1
Get all dependencies of your schema with this query:
select * from all_dependencies where owner = 'your_schema_name'
Export result of query to JSON (or any other format).
Process JSON of dependencies to generate tree(s).
I'm using Apex 4.2.4 and Oracle 11g. I have a mailing list application where I'm maintaining multiple mailing lists. A given recipient may belong to one or more lists. I'm using an Apex Shuttle to maintain the lists.
The source of all mailing recipients is in the table: mail_recipient. There are four important fields in mail_recipient:
prim_key
first_name
last_name
email_address
There is another table that stores the selected recipients for a given mailing list: mail_recipient_category: The important fields in mail_recipient_category are:
prim_key
recipient_fkey (this stores the prim_key from the mail_recipient table)
category
merge_check
There are two displayed items on the page. The category drop down list (P31_email_list) and the shuttle (P31_email_list_assignments)
The LOV for P31_email_list_assignments is:
Select last_name || ', ' || first_name || ' -- ' || email_address, prim_key from mail_recipient
order by 1;
The PL/SQL function body for the shuttle source is:
declare
emp_list apex_application_global.vc_arr2;
i number := 1;
begin
for r in (Select mr.last_name || ', ' || mr.first_name || ' -- ' || mr.email_address, mr.prim_key
From mail_recipient mr left outer join
mail_recipient_category mrc
On mr.prim_key = mrc.recipient_fkey
Where mrc.category = :P31_EMAIL_LIST)
loop
emp_list(i) := r.prim_key;
i := i + 1;
end loop;
return APEX_UTIL.TABLE_TO_STRING(emp_list, ':');
end;
There is also a single page process to update the database table mail_recipient_category. The process is executed on submit after computations and validations.
Begin
MERGE INTO MAIL_RECIPIENT_CATEGORY ss
USING (
Select
shuttle.column_value shuttle_st
, db.recipient_fkey db_st
from
table(string_to_coll(:P31_email_list_assignments)) shuttle
left outer join mail_recipient_category db
on shuttle.column_value = db.recipient_fkey
and db.category = :P31_email_list) t
on (ss.recipient_fkey = t.db_st
and ss.category = :P31_email_list
)
when matched
then
update
set
ss.merge_check = ss.merge_check
delete
where
t.shuttle_st is null and ss.category = :P31_email_list
-- t.shuttle_st is null
when not matched
then
insert
(recipient_fkey, category)
values
(t.shuttle_st, :P31_email_list);
end;
The shuttle works fine to load from left to right and save items. The problem I'm having is deselecting items from the right side to the left side of the shuttle. After moving a a mail recipient from the right to the left side, when I press the submit button, the items don't leave the right side of the shuttle and the process doesn't delete the row from the mail_recipient_category table.
Thanks for your help with this.
After working with a very experienced colleague, we determined that the page process was at fault. Specifically, the Delete clause of the Merge statement doesn't seem to work for this shuttle. We ended up adding a separate Delete statement after the the Merge statement in the page process.
I also dropped the mail_recipient_category table and am now using the more meaningful name mail_recipient_list.
The shuttle field name is (p31_email_list_assignments).
The working shuttle LOV Definition code is:
Select last_name || ', ' || first_name || ' -- ' || email_address, prim_key
From mail_recipient
order by 1;
The working shuttle Source code is:
declare
mail_list apex_application_global.vc_arr2;
i number := 1;
begin
for r in (Select mr.last_name || ', ' || mr.first_name || ' -- ' || mr.email_address, mr.prim_key
From mail_recipient mr left join
mail_recipient_list mrl
On mr.prim_key = mrl.recipient_fkey
Where mrl.list = :P31_EMAIL_LIST)
loop
mail_list(i) := r.prim_key;
i := i + 1;
end loop;
return APEX_UTIL.TABLE_TO_STRING(mail_list, ':');
end;
The working After Submit page process code is:
Begin
Merge Into MAIL_RECIPIENT_LIST mrl
Using (
Select
shuttle.column_value shuttle_cv
, db.recipient_fkey db_rfk
From
table(string_to_coll(:P31_email_list_assignments)) shuttle
left outer join mail_recipient_list db
on shuttle.column_value = db.recipient_fkey
and db.list = :P31_email_list) t
On (mrl.recipient_fkey = t.db_rfk
And mrl.list = :p31_email_list
)
When Matched
Then
Update
Set
mrl.merge_check = mrl.merge_check
-- Delete
-- Where
-- t.shuttle_cv is null
When Not Matched
Then
Insert
(recipient_fkey, list)
Values
(t.shuttle_cv, :P31_email_list);
/* The commented-out delete clause of the Merge statement never worked with this shuttle.
The following delete statement will every time this page process is called
*/
Delete from MAIL_RECIPIENT_LIST
Where instr(':' || :P31_email_list_assignments || ':',':' || recipient_fkey || ':') = 0
And list = :P31_email_list;
end;
I hope someone finds this useful.
I would like this SQL to be converted to LINQ. (it shouldl select rows from input which do not exist in table production based on 3 columns. If a column in both tables contains NULL, it should be considered as having the same value)
SELECT i.* FROM INPUT AS i
WHERE NOT EXISTS
(SELECT p.Agent FROM Production AS p
WHERE ISNULL(i.CustID,'') <> ISNULL(p.CustID,'')
AND ISNULL(i.CustName,'') <> ISNULL(p.CustName,'')
AND ISNULL(i.household,'') <> ISNULL(p.Household,''))
First of all - this is not a good SQL query. Every column is wrapped in a non-sargable function which means that the engine won't be able to take advantage of any indexes on any of those columns (assuming you have any).
Let's start by rewriting this as a semi-decent SQL query:
SELECT i.*
FROM Input i
LEFT JOIN Production p
ON (p.CustID = i.CustID OR (p.CustID IS NULL AND i.CustID IS NULL))
AND (p.CustName = i.CustName OR (p.CustName IS NULL AND i.CustName IS NULL))
AND (p.Household = i.Household OR
(p.Household IS NULL AND i.Household IS NULL))
WHERE p.CustID IS NULL
Now having said this, LEFT JOIN / IS NULL is not great for efficiency either, but we don't have much choice here because we're comparing on multiple columns. Based on your column names, I'm starting to wonder if the schema is properly normalized. A CustID should most likely be associated with one and only one CustName - the fact that you have to compare both of these seems a bit odd. And Household - I'm not sure what that is, but if it's a varchar(x)/nvarchar(x) column then I wonder if it might also have a 1:1 relationship with the customer.
If I'm speculating too much here then feel free to dismiss this paragraph; but just in case, I want to say that if this data isn't properly normalized, normalizing it would make it much easier and faster to query on:
SELECT *
FROM Input
WHERE CustID NOT IN (SELECT CustID FROM Production)
Anyway, going back to the first query, since that's what we have to work with for now. Unfortunately it's impossible to create a join on those specific conditions in Linq, so we need to rewrite the SQL query as something slightly worse (because we now have to read from Input twice):
SELECT *
FROM Input
WHERE <Primary Key> NOT IN
(
SELECT i.<Primary Key>
FROM Input i
INNER JOIN Production p
ON (p.CustID = i.CustID OR (p.CustID IS NULL AND i.CustID IS NULL))
AND (p.CustName = i.CustName OR (p.CustName IS NULL AND i.CustName IS NULL))
AND (p.Household = i.Household OR
(p.Household IS NULL AND i.Household IS NULL))
)
Now we have something we can finally translate to Linq syntax. We still can't do the join explicitly, which would be best, but we go old-school, start from the cartesian join and toss the join conditions into the WHERE segment, and the server will still be able to sort it out:
var excluded =
from i in input
from p in production
where
((p.CustID == i.CustID) || ((p.CustID == null) && (i.CustID == null))) &&
((p.CustName == i.CustName) ||
((p.CustName == null) && (i.CustName == null))) &&
((p.Household == i.Household) ||
((p.Household == null) && (i.Household == null)));
select i.PrimaryKey;
var results =
from i in input
where !excluded.Contains(i.PrimaryKey)
select i;
I'm assuming here that you have some sort of primary key on the table. If you don't, you've got other problems, but you can get around this particular problem using EXCEPT:
var excluded =
from i in input
from p in production
where
((p.CustID == i.CustID) || ((p.CustID == null) && (i.CustID == null))) &&
((p.CustName == i.CustName) ||
((p.CustName == null) && (i.CustName == null))) &&
((p.Household == i.Household) ||
((p.Household == null) && (i.Household == null)));
select i;
var results = input.Except(excluded);