Bool support Oracle SQL - sql

It's a constant frustration of mine that Oracle PL/SQL supports the bool data-type, while Oracle SQL does not. It's a big pain in the proverbial when you want to process a PL/SQL boolean return-value back into your everyday SQL (example below).
Even the ask-Tom website is blasé about this misfit, reporting that you should code boolean columns as fixed-values 'Y'/'N' CHAR columns, which is a such a bad cop-out answer on so many different levels that I don't know where to start criticising it. In fact, the only redeeming quality of this response is the fact that (as far as I've recently discovered), many other database-engines don't support the boolean data-type either.
Anyhow - the question...
I have a work-around for the following problem (albeit messy and verbose), so I'm asking this question out of curiosity rather than necessity. But one of the few things that surprises me any more is the ingenuity of clever programmers, so here's hoping that one of you can come up with a solution to the following.
In the following sample, the function stock_pkg.is_in_stock() (which is an inherent part of my application) returns a BOOL value, rendering the SQL invalid (remember, SQL doesn't support BOOL):
SELECT part_no, stock_pkg.is_in_stock(part_no) in_stock
FROM parts_table
What I need is to find a way of using the above function-call to generate a valid string (varchar) output of the format:
PART_NO IN_STOCK
------- ------------
AA YES
BB NO
CC NO
(You may substitute 'yes/no' for 'true/false', 'green/red', 'tory/labour' or even numeric 1/0 for all I care - just so long as the output falls into one of two distinct categories.)
Unfortunately, I don't have the privilege to rewrite the original function to return a different data-type. And besides, there are thousands of functions like this dotted around the larger application, making it impractical to rewrite them all.
So in this sense, the solution must be a 'generic' one (i.e. not specific to this function call). For example, it is not sufficient to rewrite the function as stock_pkg.is_in_stock_chr(), because that would mean having to re-write all the other similar functions in my application too.
I've already tried:
SELECT part_no,
CASE WHEN stock_pkg.is_in_stock(part_no) THEN 'y' ELSE 'n' END in_stock
FROM parts_table
and even my own wrapper function:
SELECT part_no,
my_bool_to_str(stock_pkg.is_in_stock(part_no)) in_stock
FROM parts_table
But even wrapping booleans inside other functional constructs doesn't seem to be allowed by Oracle SQL (at least not in Oracle 10g).
There's also the option of writing a sub-select inside the in_stock column, but that could get excessively complicated in extreme examples too, and would also be case-specific.
As I say, I hope there's an ingenious solution out there somewhere (or at least a very simple one which I happen to have overlooked).
Thanks for your time.

You can write your own wrapper like this:
CREATE OR REPLACE FUNCTION my_bool_to_str(f varchar2) RETURN VARCHAR2 IS
b varchar2(2);
BEGIN
EXECUTE IMMEDIATE 'declare bl boolean; begin bl := ' || f ||
'; if bl then :1 := ''y''; else :1 := ''n''; end if; end;'
using out b;
return b;
END;
Then you can call it like this:
SELECT part_no,
my_bool_to_str('stock_pkg.is_in_stock('|| part_no|| ')') in_stock
FROM parts_table
The difference from your wrapper is that it gets a varchar as input and not a boolean which the SQL engine doesn't recognize

Related

Iterating through select result in PostgreSQL without PL/pgSQL

I am currently working on a system built to handle the different reservations of classes and cubicles in a school building. I have it such a way that when you are reserving a classroom you are automatically reserving every cubicle inside of it.
I need to iterate through the result of a select in pure SQL without the use of PL/pgSQL to achieve this reservation as it's not exactly clear how many cubicles a classroom has prior to querying it.
I've already tried PL/pgSQL, and while it does work well, I would need an approach that doesn't use PL/pgSQL for class requirements. Here's a sample of my PL/pgSQL implementation with the FOR keyword.
CREATE OR REPLACE FUNCTION createReservationClass(p_codeBuilding CHAR(2),p_codeClass INT,p_CIP VARCHAR(8),p_date DATE,p_startPeriod INT,p_endPeriod INT,p_description VARCHAR(1024))
RETURNS VOID
AS $$
FOR temprow IN SELECT CODE_CUBICULE FROM public.CUBICULE where CODE_BUILDING = p_codeBuilding AND CODE_CLASS = p_codeClass
LOOP
SELECT createReservationCubicule(p_codeBuilding, p_codeClass, temprow.CODE_CUBICULE, p_CIP, p_date, p_startPeriod, p_endPeriod, p_description);
END LOOP;
END;
$$
LANGUAGE PLPGSQL;
I'd like it that when I am given a building number and a classroom number, it will automatically reserve every cubicle it also has. I'm not sure if I have the right approach of if there is an easier way to go about it.
The only requirement is I am not allowed to use PL/pgSQL to write my function.
Why the weird requirement not to use PL/pgSQL? However, you can easily do it with an SQL function.
Stop thinking procedurally and start thinking in SQL. What you want to do can be done with a single query without (explicit) loops:
SELECT createReservationCubicule(
p_codeBuilding,
p_codeClass,
CODE_CUBICULE,
p_CIP,
p_date,
p_startPeriod
p_endPeriod,
p_description
)
FROM public.CUBICULE
where CODE_BUILDING = p_codeBuilding
AND CODE_CLASS = p_codeClass;

Best practices for writing / debugging complex PL/pgSQL queries

In short: What is the best way to write and debug PostgreSQL queries that make use of procedural programming techniques (e.g. using pl/pgsql)? I'm pretty new to writing these types of procedural queries so apologies in advance if there is a really simple solution I haven't found.
Specifics: I am writing some (relatively) complex SQL queries that make use of existing pl/pgsql functions and ideally would make use of lots of variables to make the query easy to debug and modify. (The example code below is deliberately simple; my actual use cases are much more complicated.)
My first solution was to embed everything inside a function, but this is clunky. Since I have to declare the return type explicitly, it makes modifying the code a pain:
CREATE OR REPLACE FUNCTION
my_schema.my_fcn()
RETURNS TABLE(user_id integer,
id integer,
created_at timestamp) AS
$BODY$
DECLARE
_id_select integer = 10;
BEGIN
RETURN QUERY
SELECT
user_id,
id,
created_at
WHERE id = _id_selector
-- possibly a ton of other complicated stuff involving other functions and variables
FROM my_schema.my_other_fcn()
END;
$BODY$
LANGUAGE plpgsql;
(Edit: Made the query slightly more nontrivial.)
Now suppose I want to add order_total to my query and get rid of id: Then I have to modify the return type. This is going to happen a lot, and its annoying to have to change the return type over and over.
The next solution I tried was to NOT use a function, and try something along these lines: How can I execute pl/pgsql code without creating a function?
This does not work either:
DO $$
DECLARE
_id_select integer = 10;
BEGIN
SELECT
user_id,
id,
created_at
WHERE id = _id_selector
-- possibly a ton of other complicated stuff involving other functions and variables
FROM my_schema.my_other_fcn()
END $$;
The resulting error:
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function inline_code_block line 4 at SQL statement
********** Error **********
ERROR: query has no destination for result data
I do not want to use PERFORM because, actually, I do want to see the result of the query.
Is there a graceful and/or standard solution to this type of SQL programming?
Why you don't call plpgsql function directly?
do only:
SELECT
user_id,
id,
created_at
-- possibly a ton of other complicated stuff involving other functions and variables
FROM my_schema.my_other_fcn()
I don't like a this style of plpgsql programming - wrapping queries. You can block very effective planner - because plpgsql function is black box for optimizer - so you can expect terrible performance issues on large data. PLpgSQL functions should not to substitute a views.
Statement DO should not to return any result. Simply - PostgreSQL functions should not to substitute PostgreSQL views.

How do I write an SQL function that takes an unknown amount of numbers as parameters?

I am trying to write an Oracle SQL function that takes a list of numbers as arguments and return a pipelined list of table rows. My main problem is the quantity of numbers that can be passed is never certain with no real upper limit. I'll try and demonstrate what I mean:
Say I have a table defined as so:
create table items (
id number primary key,
class number,
data string
);
I want to return all rows that match one of a list of class numbers that I submit. The function I'm shooting at looks a little like this:
function get_list_items_from_class([unknown number of parameters]
in items.class%type)
return tbl_list_item pipelined; -- I have types defined to handle the return values
I've been looking at ways to handle defining a function that can take an undefined amount of integers and so far the most promising search has taken me to this page which explains about using collections and records. I don't think a VARRAY is what I'm looking for as the size has to be predefined. As Associative Array may be what I'm looking for, but before I spend a lot of time trying things out, I want to make sure the tool is fit for the job. I'm pretty inexperienced with Oracle SQL right now and I'm working on a time sensitive project.
Any help that you could offer would be appreciated. I realise that there are simpler ways to achieve what I'm trying to do in this example (simply multiple calls to a function that takes one parameter is one) but this example is simplified. Other parts of the project I'm working on require me to seek a solution using this multiple parameter method.
EDIT: That being said, I would welcome other design suggestions if I'm way off base with what I'm trying to attempt. It would be a learning experience if nothing else.
Many thanks in advance for your time.
EDIT: I will be accessing the database from proprietary client software written in Java.
You could use a table parameter as I linked in the comments or you could pass in a comma separated list of values parse it to a table and join to that.
something like this (with input_lst as a string):
select *
from tbl_list_item
where tbl_list_item.class in
(
select regexp_substr(input_lst,'[^,]+', 1, level) from dual
connect by regexp_substr(input_lst, '[^,]+', 1, level) is not null
);
adapted from https://blogs.oracle.com/aramamoo/entry/how_to_split_comma_separated_string_and_pass_to_in_clause_of_select_statement
Which choice is better depends on your expected number of entries and what is easier for your client side. I think with a small number (2-20) the comma separated is a fine choice. With a large number you probably want to pass a table.
A colleague actually suggested another way to achieve this and I think it is worth sharing. Basically, define a table type that can contain the arguments, then pass an array from the Java program that can be read from this table.
In my example, firstly define a table type of number:
create or replace type tbl_number as table of number;
Then, in the SQL package, define the function as:
function get_list_items_from_class(i_numbers in tbl_number)
return tbl_list_item pipelined;
The function in the package body has one major change (apart from the definition obviously). Use the following select statement:
select *
from tbl_list_item
where tbl_list_item.class in
(
select * from table(i_numbers)
);
This will select all the relevant items that match one of the integers that were passed to the "i_numbers" table. I like this way as it means less string parsing, both in the Java application and the SQL pacakage.
Here's how I passed the number arguments from the Java application using an ARRAY object.
ArrayDescriptor arrayDesc = ArrayDescriptor.createDescriptor("NUMBERS", con); //con is the database connection
ARRAY array = new ARRAY(arrayDesc, con, numberList.toArray()); // numberList is an ArrayList of integers which holds the arguments
array is then passed to the SQL function.

Extracting columns and table names from an oracle query that is based on dynamic SQL

I want an approach/code snippet to extract column names and the corresponding table name from an oracle query. The queries and consequently the columns and table names change at run time and some of the column names usually are calculated meaning they are wrapped in a function and aliased. I tried different string tokenizing techniques using regexp to separate this out as per the expeted output, but so far, no luck !
Eg:
select mandate_name, investment_sub_team_name,
fn_sum(REG_INV_CMP_AUM) REG_INV_CMP_AUM,
fn_sum(NON_REG_INV_CMP_AUM) NON_REG_INV_CMP_AUM
from DM_SAI_VALUATIONS_STEP3
where position_interval_type = 'E'
and position_type = 'T'
group by mandate_name, investment_sub_team_name;
I want the output for the columns as:
mandate_name
investment_sub_team_name
fn_sum(REG_INV_CMP_AUM)
fn_sum(NON_REG_INV_CMP_AUM)
Note above: I want the columns with the function and not the alias
I want the output for the table name as: DM_SAI_VALUATIONS_STEP3 against all the columns that I listed above
I cannot edit the queries as they are part of an xml output. So, i cannot change the alias. The second point is to just extract the table name from the query. Please consider the fact that nothing can be hard coded like position of the string token etc as the queries containing the columns and the table would be different. I am looking for a generic approach to tokenize them. So, against the column output that I expect, i just need the table name as well..Its always going to only one table in the from clause, so extracting that would not be an issue.
Expected output:
Column Name Table Name
----------- ----------
mandate_name DM_SAI_VALUATIONS_STEP3
investment_sub_team_name DM_SAI_VALUATIONS_STEP3
fn_sum(REG_INV_CMP_AUM) DM_SAI_VALUATIONS_STEP3
fn_sum(NON_REG_INV_CMP_AUM) DM_SAI_VALUATIONS_STEP3
Any help pr pointers would be much appreciated.
You realistically can't solve this problem in general without writing your own SQL compiler (at least the parser and lexer up through the semantic analysis phase). That is a non-trivial exercise particularly if you want to accept any valid Oracle SQL query. Oracle Corporation used to have different SQL parsers for the SQL VM and the PL/SQL VM and couldn't keep them in sync-- it's a major investment of time and effort to keep evolving your parser as the supported SQL grammar improves.
If you're really determined to go down this path, you can start with some of the ANTLR SQL grammars. The O'Reilly Flex and Bison book also has a chapter on parsing SQL that you could potentially use as a starting point. Of course, you'll need to revise and extend the grammars to support whatever SQL features your queries might contain. You'll then need to build the syntax analyzer and semantic analysis portions of the compiler to implement the appropriate scope resolution rules to be able to figure out which table a particular reference to a particular column comes from. Just to reiterate, this is a non-trivial exercise and it's one that has to be enhanced for every new release of the database.
If you can relax your requirements and make some assumptions about what sorts of queries you're going to be seeing, it becomes much easier to write a parser. If you can guarantee that every query references exactly 1 table, identifying which table a particular column comes from is much easier. If you can guarantee that every function call takes at most one column from one table as a parameter, that also makes things easier-- otherwise, you'll need to figure out what you want to return for the table name if the column is the result of a function that takes as arguments columns from multiple tables.
I also agree it is generally not possible. But maybe the solution is to get in touch with the creator of the XML message and agree on a different protocol then a finished up SELECT statement beforehand. Agree with him sending the columns.
If this is not possible and you want to make certain assumptions about how the query is built then you can tokenize after the selectand before from by using the , as a separator. But by all I know you can not really do that by regular expression substring commands. I think you need a bit of PL/SQL function written.
But still take care from keyword could be somewhere part of the columns selecting. What do you do if you suddenly get a query like this:
select
something,
(select count(*) from othertable) as cnt,
andfromthiscolumn xyz
from mytable
So my tip here is to rather sort it out organizationally then trying to code the impossible.
If you know that the structure of your query strings will not change much, you can do something like this:
set serveroutput on
set termout on
clear
declare
v_str varchar2(500) := 'select mandate_name, investment_sub_team_name,
fn_sum(REG_INV_CMP_AUM) REG_INV_CMP_AUM,
fn_sum(NON_REG_INV_CMP_AUM) NON_REG_INV_CMP_AUM
from DM_SAI_VALUATIONS_STEP3
where position_interval_type = ''E''
and position_type = ''T''
group by mandate_name, investment_sub_team_name;';
v_tmp varchar2(500);
v_cols varchar2(500);
v_table varchar2(500);
begin
v_tmp := replace( v_str, 'select ','');
v_tmp := substr( v_tmp, 1, instr(v_tmp, 'where')-1);
dbms_output.put_line('original query: '||v_str);
v_cols := substr (v_tmp, 1, instr(v_tmp, 'from')-1);
dbms_output.put_line('column names: '||v_cols);
v_table := substr(v_tmp, instr(v_tmp, 'from ')+4, 500);
dbms_output.put_line('table name: '||v_table);
end;

Why do SQL errors not show you the error source?

Is it possible to find the line or column where an error is occurring when executing SQL code in Oracle SQL developer?
For example, imagine you are running a very simple line of code
SELECT * FROM employeesTbl WHERE active = 1
But for some reason, active is VARCHAR and someone has entered the ";!/asd02" into this field.
You will only get an ORA- error, but it does not tell you which row caused it.
Does anyone know why this is?
The reason behind this is that in general developer support in sql, pl/sql and the like is really abysmal. One result is a really broken exception concept in pl/sql, almost useless exceptions in (oracle) sql and little hope that it is better in any rdbms.
I think the reason behind all that is that databases are persistent beasts (pun intended). Many companies and developers change from time to time there preferred main development language (C, C++, VB, Java, C#, Groovy, Scala ..). But they rarely change the database, possibly because you will still have the old databases around with no chance to migrate them.
This in turn means most DB-devs know only a single database system reasonable well, so they don't see what is possible in other systems. Therefore there is little to no pressure to make database systems any more usable for developers.
Multiple rows may contain errors. For the system to be consistent (as a "set-based" language), it ought to return you all rows which contain errors - and not all row errors may be caused by the same error.
However, it could be computationally expensive to compute this entire error set - and the system "knows" that any further computation on this query is going to result in failure anyway - so it represents wasted resources when other queries could be running successfully.
I agree that it would be nice to turn on this type of reporting as an option (especially in non-production environments), but no database vendor seems to have done so.
You get an error because the field is a character and you're assuming it's a number. Which, you shouldn't be doing. If you want the field to be numeric then you have to have a numeric field! This is a general rule, all non-character columns should be the correct data-type to avoid this type of problem.
I'm not certain why Oracle doesn't tell you what row caused the error, it may be physically possible using the rowid in a simple select as you have here. If you're joining tables or using conversion functions such as to_number it would become a lot more difficult, if possible at all.
I would imagine that Oracle did not want to implement something only partially, especially when this is not an Oracle error but a coding error.
To sort out the problem create the following function:
create or replace function is_number( Pvalue varchar2
) return number is
/* Test whether a value is a number. Return a number
rather than a plain boolean so it can be used in
SQL statements as well as PL/SQL.
*/
l_number number;
begin
-- Explicitly convert.
l_number := to_number(Pvalue);
return 1;
exception when others then
return 0;
end;
/
Run the following to find your problem rows:
SELECT * FROM employeesTbl WHERE is_number(active) = 0
Or this to ignore them:
SELECT *
FROM ( SELECT *
FROM employeesTbl
WHERE is_number(active) = 1 )
WHERE active = 1