How to implement NOT EXISTS in OPEN QUERY statement in PROGRESS 4GL - OpenEdge 10.2A - openedge

I want to create a browse such that it will show all the records from one table if the values of a field do NOT exist in another table.
It is possible to get the records using SQL as:
SELECT myField FROM pub.myTable WHERE
NOT EXISTS (SELECT myField FROM pub.myTable2 WHERE myTable2.myField=myTable.myField)
It is also possible using 4GL as:
FOR EACH myTable WHERE
NOT CAN-FIND(FIRST myTable2 WHERE myTable2.myField=myTable.myField)
The problem is when I put this query in a browse as:
OPEN QUERY myBrowse
FOR EACH myTable WHERE
NOT CAN-FIND(FIRST myTable2 WHERE myTable2.myField=myTable.myField)
it gives an error message
CAN-FIND is invalid within an OPEN QUERY. (3541)
The question is, is it possible to write such an OPEN QUERY statement?

I didn't come up with this, Steve Moore shared it on https://community-archive.progress.com/forums/00026/27143.html
define temp-table ttNoOrder
field field1 as char.
create ttNoOrder.
define query q1 for Customer, Order, ttNoOrder.
open query q1 for each Customer no-lock,
first Order of Customer outer-join no-lock,
first ttNoOrder where not available(Order).
get first q1.
repeat while not query-off-end("q1"):
display Customer.CustNum Customer.Name available(Order).
get next q1.
end.

Works even with dynamic queries:
DEFINE TEMP-TABLE ttNoOrder
FIELD field1 AS CHARACTER .
DEFINE VARIABLE hQuery AS HANDLE NO-UNDO.
CREATE ttNoOrder.
CREATE QUERY hQuery .
hQuery:SET-BUFFERS (BUFFER Customer:HANDLE,
BUFFER Order:HANDLE,
BUFFER ttNoOrder:HANDLE) .
hQuery:QUERY-PREPARE ("for each Customer no-lock, ~
first Order of Customer outer-join no-lock, ~
first ttNoOrder where not available(Order)") .
hQuery:QUERY-OPEN() .
hQuery:GET-FIRST () .
REPEAT WHILE NOT hQuery:QUERY-OFF-END:
DISPLAY Customer.CustNum FORMAT ">>>>>>>>>9" Customer.Name AVAILABLE(Order).
hQuery:GET-NEXT ().
END.

Related

What is the query for getting database name in Progress-OpenEdge?

How can we get database name in progress openedge like in SQL we can get database name by using show databases; or SELECT schema_name FROM information_schema.schemata; using SQL query to openedge DB.
In ABL you can
DEFINE VARIABLE i AS INTEGER NO-UNDO.
DO i = 1 TO NUM-DBS:
MESSAGE LDBNAME (i) SKIP
PDBNAME (i) SKIP
DBPARAM (i)
VIEW-AS ALERT-BOX INFORMATION BUTTONS OK.
END.
In OpenEdge SQL the SHOW CATALOGS command returns the database/catalog names:
https://docs.progress.com/bundle/openedge-sql-reference/page/SHOW-CATALOGS.html?_ga=2.234385114.558448476.1620632697-128156788.1596090319
SHOW CATALOGS PRO_NAME;
select db_name() from sysprogress.syscalctable;
Just in case the link to "Progress Communities" goes away:
Valeriy Bashkatov (Progress Technologies LLC)
You should run this with a DBA privileged user.
select * from sysprogress.SYSTABLES;
select * from sysprogress.SYSTABLES_FULL;
select * from sysprogress.SYSCOLUMNS where TBL = 'table_name';
select * from sysprogress.SYSCOLUMNS_FULL where TBL = 'table_name';

bulk insert using insert...select in sqlite?

I have a list of users who should receive the message. They are in the table subscribe. Now I'd like to insert a message for every one of these users. My query is
insert into message(user, type, theId)
select (select user from subscribe_message), #type, #id
At the moment it is empty. I get the error message.user may not be NULL. Shouldn't it not insert any rows? When I have more than one row it inserts the first row only. How do I write this so it will insert 0 to many rows?
Try this
INSERT INTO message ( user, type, theId )
SELECT subscribe_message.user, #type, #id
FROM subscribe_message

Pl/SQL procedure with single Parameter

I have create the following tables...
CREATE TABLE Actor
(Actor_ID CHAR(5),
lastName CHAR(24),
firstName CHAR(24),
/
CREATE TABLE Movie
(movieID CHAR(3) ,
title CHAR(36),
year NUMBER,
/
CREATE TABLE Role
(roleID CHAR(5),
roleName CHAR(36),
actorID CHAR(5),
movieID CHAR(3))
/
CREATE TABLE Quote
(quoteID CHAR(4),
quoteCHAR CHAR(255))
/
CREATE TABLE RoleQuote
(roleID CHAR(5),
quoteID CHAR(4))
/
Then i created this schemas....
CREATE TYPE ACTOR_QUOTE_TYPE AS OBJECT (
Movie_Title CHAR(36),
Year NUMBER,
Role CHAR(36),
Quote CHAR(255)
)
/
CREATE TYPE AQ_NT AS TABLE OF ACTOR_QUOTE_TYPE
/
CREATE TABLE ACTOR_QUOTES (
ACTORID CHAR(5),
QUOTES AQ_NT
) NESTED TABLE QUOTES STORE AS ACTOR_QUOTES_NT
/
I need to create a procedure with a single parameter(ACTORID is procedure parameter) and insert all the quotes in all the movies for any ACTORID, into the row(s) (an actor may have many movies and many quotes, some may have no quotes!) of the QUOTES nested table in the ACTOR_QUOTES table for any ACTORID.
How do i do it ?
Thanks
So far i tried this, i am not sure it is correct or not.
CREATE OR REPLACE PROCEDURE Populate_Movies_Quote
AS
CURSOR Quote_cursor (ActorID in CHAR) IS
SELECT ActorID, Quote, Movie_Title from Actor_Quotes, AQ_NT where Quotes.ActorID=ActorID;
BEGIN
FOR row IN Quote_cursor
LOOP
INSERT INTO ACTOR_QUOTES (ActorID, quotes) values (row.ActorID, AQ_NT(Actor_Quote_Type));
END LOOP;
END Populate_Movies_Quote ;
/
Show erros
LINE/COL ERROR
-------- -----------------------------------------------------------------
4/1 PL/SQL: SQL Statement ignored
4/55 PL/SQL: ORA-04044: procedure, function, package, or type is not
allowed here
6/1 PL/SQL: Statement ignored
6/10 PLS-00306: wrong number or types of arguments in call to
'QUOTE_CURSOR'
This certainly looks familiar. I take it someone else is stuck with Paul Judges Assignment too.
Without doing this for you here's the basic strategy I took.
First just write a select query that returns Movie Title, Movie Year, Role Name, and the Quote for a given Actor ID in that order. Forget about the procedure for now just get this select statement working. It means joining all the tables in the where clause.
If you achieve that then you basically have all the data that needs to be inserted into the nested table.
You can access the nested table for insertion by using the table function. So something like:
INSERT INTO TABLE(SELECT QUOTES FROM Actor_Quotes WHERE ActorID = Actor_ID)
Where "Actor_ID" is the name of your procedures parameter. PL/SQL actually lets you insert into a table values directly from a select statement. You just have to ensure the values returned by the select statement match the order and type that your insert statement is expecting. This is pretty handy as it means there is no need for a cursor loop. So essentially all you have to do is place the select statement I said to write earlier directly below the above insert statement and you should be sorted. Make sure you use the same Actor_ID Parameter in your select query though.

dynamic table name in a query

How can I put a table name dynamically in a query?
Suppose I have a query as shown below:
Select a.amount
,b.sal
,a.name
,b.address
from alloc a
,part b
where a.id=b.id;
In the above query I want to use a table dynamically (part b if the database is internal, p_part b if the database if external).
I have a function that returns which database it is. Suppose the function is getdatabase();
select decode(getdatabase(),'internal','part b','external','p_part b')
from dual;
How can I use this function in my main query to insert the table name dynamically into the query?
I don't want to implement this using the primitive way of by appending strings to make a final query and then open cursor with that string.
I don't want to implement this with primitive way of by appending
strings to make a final query and then open cursor with that string .
That's really the only way you can do it. It's not possible to use a variable or function call for the table name when using a regular PL/SQL SQL block, you have to use dynamic SQL.
Refer to Oracle documentation for more details:
http://docs.oracle.com/cd/B10500_01/appdev.920/a96590/adg09dyn.htm
Here's an example from the doc:
EXECUTE IMMEDIATE 'SELECT d.id, e.name
FROM dept_new d, TABLE(d.emps) e -- not allowed in static SQL
-- in PL/SQL
WHERE e.id = 1'
INTO deptid, ename;
You can do this without dynamic SQL, assuming both tables (part and p_part) are available at compile time:
select a.amount
,b.sal
,a.name
,b.address
from alloc a
,part b
where a.id=b.id
and (select getdatabase() from dual) = 'internal'
UNION ALL
select a.amount
,b.sal
,a.name
,b.address
from alloc a
,p_part b
where a.id=b.id
and (select getdatabase() from dual) = 'external'
;
I've put the function call in a subquery so that it is run only once per call (i.e. twice, in this instance).

How to handle a missing feature of SQLite : disable triggers?

How to handle a missing feature of SQLite: disable triggers?
I don't have it stored the name of triggers for a specific table.
For example how can I drop all triggers?
What would you do?
So here it is 2015 and there still is no 'disable triggers' in SQLite. For a mobile Application this can be problematic--especially if it's a corporate App requiring offline functionality and local data.
An initial data load can be slowed to crawl by trigger execution even when you don't wrap each insert in an individual transaction.
I solved this issue using SQLite SQL fairly simply. I have a settings table that doesn't participate in the init load. It holds 'list' of key/value pairs. I have one key called 'fireTrigger' with a bit value of 0 or 1. Every trigger I have has an expression that selects value and if it equals 1 it fires the trigger, otherwise it doesn't.
This expression is in addition to any expressions evaluated on the data relating to the trigger. e.g.:
AND 1 = (SELECT val FROM MTSSettings WHERE key = 'fireTrigger')
In simple clean effect this allows me to disable/enable the trigger with a simple UPDATE to the settings table
I wrote a very simple extension function to set a boolean value to true or false.
And a function to retrieve this value (GetAllTriggersOn()).
With this function I can define all my triggers like:
CREATE TRIGGER tr_table1_update AFTER UPDATE ON TABLE1 WHEN GetAllTriggersOn()
BEGIN
-- ...
END
SQLite stores schema (meta) information in the built-in sqlite_master table.
To get a list of available triggers use the below query:
SELECT name FROM sqlite_master
WHERE type = 'trigger' -- AND tbl_name = 'a_table_name'
Set a flag in your database and use it in the triggers WHEN condition.
Say you want to create a trigger on the "clients" table after an insert. You have created a table "trigger_settings" with a TINYINT "triggers_on" field - this is your flag. Then you can set the field to 0 if you want to turn off the filters and to 1 when you want to turn them back on.
Then you create your filter with a WHEN condition that checks the "triggers_on" field.
For example:
CREATE TRIGGER IF NOT EXISTS log_client_data_after_insert
AFTER INSERT
ON [clients]
WHEN (SELECT triggers_on FROM trigger_settings)=1
BEGIN
your_statement
END;
Maybe you can make a stored procedures for droping and creating them. Is that good for you ?
Expanding on Nick Dandoulakis's answer, you could drop all relevant triggers and then reinstate them before the transaction's completion:
BEGIN;
SELECT name, sql FROM sqlite_master WHERE type = 'trigger' AND tbl_name = 'mytable';
-- store all results
-- for each name: DROP TRIGGER $name;
-- do normal work
-- for each sql: execute the SQL verbatim
COMMIT;
Expanding other answers this is how i'm doing it. Take into account that this is disabling all triggers for all tables in the database except some of then used by spatialite
SQLITE_FILE=/tmp/my.sqlite
# Define output sql files as variables
CREATE_TRIGGER_SQL=/tmp/create_triggers.sql
DROP_TRIGGER_SQL=/tmp/drop_triggers.sql
## Dump CREATE TRIGGER statements to a file ##
# To wrap statements in a transaction
echo -e "BEGIN;\n\n" > "${CREATE_TRIGGER_SQL}"
# `SELECT sql` does not output semicolons, so we must concatenate them
sqlite3 -bail "${SQLITE_FILE}" "SELECT sql || ';' FROM sqlite_master WHERE type = 'trigger' AND (name NOT LIKE 'gid_%' AND name NOT LIKE 'ggi_%' AND name NOT LIKE 'ggu_%' AND name NOT LIKE 'gii_%' AND name NOT LIKE 'giu_%' AND name NOT LIKE 'vwgcau_%' AND name NOT LIKE 'vtgcau_%' AND name NOT LIKE 'gcau_%' AND name NOT LIKE 'geometry_columns_%' AND name NOT LIKE 'gcfi_%' AND name NOT LIKE 'gctm_%' AND name NOT LIKE 'vtgcfi_%' AND name NOT LIKE 'vwgcfi_%' AND name NOT LIKE 'vtgcs_%' AND name NOT LIKE 'vwgc_%' AND name NOT LIKE 'vtgc_%' AND name NOT LIKE 'gcs_%');" >> "${CREATE_TRIGGER_SQL}"
echo -e "\n\nCOMMIT;" >> "${CREATE_TRIGGER_SQL}"
## Dump DROP TRIGGER statements to a file ##
echo -e "BEGIN;\n\n" > "${DROP_TRIGGER_SQL}"
sqlite3 -bail "${SQLITE_FILE}" "SELECT 'DROP TRIGGER ' || name || ';' FROM sqlite_master WHERE type = 'trigger' AND (name NOT LIKE 'gid_%' AND name NOT LIKE 'ggi_%' AND name NOT LIKE 'ggu_%' AND name NOT LIKE 'gii_%' AND name NOT LIKE 'giu_%' AND name NOT LIKE 'vwgcau_%' AND name NOT LIKE 'vtgcau_%' AND name NOT LIKE 'gcau_%' AND name NOT LIKE 'geometry_columns_%' AND name NOT LIKE 'gcfi_%' AND name NOT LIKE 'gctm_%' AND name NOT LIKE 'vtgcfi_%' AND name NOT LIKE 'vwgcfi_%' AND name NOT LIKE 'vtgcs_%' AND name NOT LIKE 'vwgc_%' AND name NOT LIKE 'vtgc_%' AND name NOT LIKE 'gcs_%');" >> "${DROP_TRIGGER_SQL}"
echo -e "\n\nCOMMIT;" >> "${DROP_TRIGGER_SQL}"
## Execute like ##
sqlite3 -bail /"${SQLITE_FILE}" < "${DROP_TRIGGER_SQL}"
# do things
sqlite3 -bail /"${SQLITE_FILE}" < "${CREATE_TRIGGER_SQL}"

Resources