Issue when using decode in SQL query where clause - sql

I have been mulling over a problem for the last few hours with a query which executes a decode statement as part of my WHERE CLAUSE.
The best Example I can come up with here is as follows:
--This variable is normally populated by a query.
--Debugger confirms this is set to 'US' with no leading or tailing characters
Str_MyVar varchar2(10) := 'US';
-- Query 1 returns 16 rows (incorrect)
Select *
From myTable, table2
Where myTable.value = decode(Str_MyVar, 'US', table2.value, 0)
-- Query 2 returns 1 rows (correct)
Select *
From myTable, table2
Where myTable.value = decode('US', 'US', table2.value, 0)
Now then, if I change the query which populates the Str_MyVar variable to instead convert the value 'US' to a 1 and all other values to a 0 and store it in a numeric variable things begin to work.
--This variable is normally populated by a query.
--Debugger confirms this is set to 1 with no leading or tailing characters
nbr_Myvar number := 1;
-- Query 1 returns 1 rows (correct)
Select *
From myTable, table2
Where myTable.value = decode(nbr_MyVar, 1, table2.value, 0);
This second example using a numeric data type for the variable in the decode works properly. The question is Why? Sure I've coded around the issue but I'd like to understand if this is an oracle bug or just a quirk of the DECODE function. Database is oracle 10.2.0.3

Related

Biq query: Resources exceeded during query execution

Hi we have a SP in Bigquery with parameters and can expect length more than 100k
CREATE OR REPLACE PROCEDURE `Procname`(Criteria ARRAY<STRING>, Criteria2 STRING)
BEGIN
DECLARE Total INT64 DEFAULT 0;
DECLARE Invalid STRING ;
DECLARE InvalidCount INT64 ;
--#1 Get Total
SET Total = (SELECT SUM(numbers) FROM `table` WHERE Number IN UNNEST (Criteria));
--#2 Get List of numbers not found and return with comma seperated
execute immediate concat("SELECT STRING_AGG(Invalid) AS Invalid, count(Invalid)
as InvalidCount FROM (SELECT Invalid FROM(SELECT '' as Invalid union all select ",Criteria2," )v
EXCEPT DISTINCT SELECT Postcode FROM `table`)") INTO Invalid,InvalidCount;
SELECT Total, Invalid, InvalidCount;
END;
where my Criteria is an array of string and Criteria2 is a string'number1' union all select 'number2' union all select 'invalidnumber' union all select 'number3' union all select 'anotherinvalidnumber'
so as per above SP say with Criteria as ['number1', 'number2','invalidnumber','number3' ,'anotherinvalidnumber'] and Criteria2 as above the result should be
The problem is on Criteria2 parameter as soon as I increase this string length I got this error as
Message[Resources exceeded during query execution: Not enough
resources for query planning - too many subqueries or query is too
complex..
which is believe is due to a lot of select '' union all.
two questions
Any better way to write query #2 taking performance into account, we tried with temp tables but performance is an issue. If we go with this approach then Bigquery API is not liking it
How to get rid of this error

How to find number of times a values is repeated in a string in a column

In SQL HANA, I need to find how many times a given word is repeated in a string column whose values are delimited by "," and output it as a separate column.
Example, the string column contains:
ZN,ZN,ZS,ZQ
Expected result for "ZN":
2
You might find it acceptable to search only the string ZN by ignoring the fact that there's a comma.
You may count the number of occurrences of any substring by using the string function OCCURRENCES_REGEXPR:
SELECT OCCURRENCES_REGEXPR('(ZN)' IN STRINGCOLUMN) "occurrences_zn" FROM TABLE;
If you really want to clearly specify that ZN is to be searched as an entire word between commas or at the edges, then you may find a better regular expression (the question is then more about regular expressions and not SQL HANA, and you may find existing answers in Stack Overflow).
I can't remember where I found the trick, but in SQL Server, the following works like a charm:
DECLARE #myStringToSearch nvarchar(250) = 'ZN,ZN,ZS,ZQ'
DECLARE #searchValue nvarchar(5) = 'ZN'
SELECT (LEN(#myStringToSearch) - LEN(REPLACE(#myStringToSearch, #searchValue, ''))) / LEN(#searchValue)
The last line compares the length of the original string with the length of the same string, but this time replacing your search value (ZN) with a blank string. In our case, this would result in 4, because ZN is 2 characters, and it was removed twice. However, we're not interested in how many characters were removed, but in how many times the value was encountered, so we divide that result by the length of your search string (2).
Output of the query:
2
You could easily implement this as a DEFAULT constraint in your table, provided your search string is the same across every row.
I wrote one anonymous block in sql , which can be converted to HANA Table function and can be used to achieve expected result.
DO
BEGIN
DECLARE FULL_STRING VARCHAR(100);
DECLARE TRIM_STRING VARCHAR(100);
DECLARE VAL_STRING VARCHAR(100);
FULL_STRING ='ZN,ZN,ZS,ZQ';
FULL_STRING=CONCAT(FULL_STRING,',');
--SELECT :FULL_STRING FROM DUMMY;
VAL_STRING=SUBSTRING(:FULL_STRING,1,LOCATE(:FULL_STRING,',',1)-1);
VAR_TABLE=SELECT :VAL_STRING STRINGVAL FROM DUMMY;
TRIM_STRING=SUBSTRING(:FULL_STRING,LOCATE(:FULL_STRING,',',1)+1 ,LENGTH(:FULL_STRING));
--SELECT * FROM :VAR_TABLE;
--SELECT :TRIM_STRING FROM DUMMY;
WHILE :TRIM_STRING IS NOT NULL AND LENGTH(:TRIM_STRING)>0
DO
VAL_STRING=SUBSTRING(:TRIM_STRING,1,LOCATE(:TRIM_STRING,',',1)-1);
--SELECT :VAL_STRING FROM DUMMY;
VAR_TABLE=SELECT STRINGVAL FROM :VAR_TABLE
UNION ALL
SELECT :VAL_STRING FROM DUMMY;
TRIM_STRING=SUBSTRING(:TRIM_STRING,LOCATE(:TRIM_STRING,',',1)+1 ,LENGTH(:TRIM_STRING));
--i=i+1;
--SELECT :TRIM_STRING FROM DUMMY;
END WHILE ;
SELECT STRINGVAL,COUNT(STRINGVAL) FROM :VAR_TABLE GROUP BY STRINGVAL;
--SELECT :TRIM_STRING FROM DUMMY;

How to do a conditional where clause with where in PL/SQL within Procedure

I have a pretty simple Stored Procedure that I am in trouble to do because i'm new to SQL and PL/SQL. I Have a table with a name column that is a varchar(55).
I discovered that if the user executes my procedure with an empty string as a paramter the LIKE statment brings all rows from TABLE1
SELECT *
FROM TABLE1
WHERE COLUMN LIKE VARIABLE || '%'
AND...
So I tried to change the query so if the VARIABLE is passed with a empty string it can still perform other conditions in the where statment.
SELECT *
FROM TABLE1
WHERE (VARIABLE <> '' AND COLUMN LIKE VARIABLE || '%')
AND...
But now wherever I pass as variable ('', NULL, 'anystring') I get no rows returned.
How can I build a query that validates if the variable is different of empty string and if it is it performs the LIKE statment with the variable correctly?
If I understand you correctly, it is not difficult thing to do. You can use conditional WHERE clause using CASE WHEN. So your query will support different scenarios, something like this:
SELECT *
FROM TABLE1
WHERE (CASE WHEN variable IS NULL AND column IS NULL THEN 1
WHEN variable LIKE '%' AND column LIKE variable||'%' THEN 1
ELSE 0
END) = 1
AND...
Basically, it checks if the variable = '' then it will compare the column against ''. Otherwise, it will compare it against variable||'%'.
Notice, Oracle treats empty string of the type VARCHAR as NULL (this does not apply to CHAR). So, in the first scenario we compare against NULL.
Hello Just a thought for this we can use Dynamic sql too. If you may try this approach. Hope it helps.
CREATE OR REPLACE PROCEDURE SPS_TEST_OUT(
p_input_in IN VARCHAR2
)
AS
lv_sql LONG;
lv_where VARCHAR2(100);
BEGIN
lv_where:= CASE WHEN p_input_in IS NULL OR p_input_in = '' THEN
''
ELSE
' AND COLUMN1 LIKE '''||p_input_in||'''%'
END;
lv_sql:='SELECT * FROM TABLE
WHERE 1 = 1
' ||lv_where;
dbms_output.put_line(lv_sql);
END;

sql server: MERGE has unexpected results

The way these rows usually come into the target table the first time are with a sparse number of columns populated with mostly text data with the remainder of the columns set to NULL. On subsequent passes, the fresh data populates existing known (non null) and unknown (NULL) data. I've ascertained that the fresh data ( #pld) do indeed contain different data. The data does not appear to change. Here's what I have:
BEGIN TRANSACTION
BEGIN TRY
MERGE INTO [metro].listings AS metroList
USING #pld as listnew
ON metroList.id = listnew.id
AND metroList.sid = listnew.sid
WHEN MATCHED AND (
metroList.User != listnew.User
or metroList.Email != listnew.Email
or metroList.LocName != listnew.LocName
) THEN
UPDATE SET
metroList.User = listnew.User,
metroList.Email = listnew.Email,
metroList.LocName = listnew.LocName,
WHEN NOT MATCHED THEN
INSERT
( User,
Email,
LocName
)
VALUES
(
listnew.User,
listnew.Email,
listnew.LocName
);
COMMIT TRANSACTION
END TRY
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH
I've tried replacing the != to under the update portion of the statement with <> . Same results. This has to be related to a comparison of a possible (likely) null value against a string--maybe even another null? Anyway, I'm calling on all sql-geeks to untangle this.
Also you can use option with NULLIF() function.
NULLIF returns the first expression if the two expressions are not equal. If the expressions are equal, NULLIF returns a null value of the type of the first expression.
WHEN MATCHED AND (
NULLIF(ISNULL(metroList.[User],''), listnew.[User]) IS NOT NULL
OR NULLIF(ISNULL(metroList.Email, ''), listnew.Email) IS NOT NULL
OR NULLIF(ISNULL(metroList.LocName, ''), listnew.LocName) IS NOT NULL
)
THEN
Comparing NULL with an empty string will not work.
If either side could be NULL, you could do something like:
WHEN MATCHED AND (
COALESCE(metroList.User, '') <> COALESCE(listnew.User, '')
or COALESCE(metroList.Email, '') <> COALESCE(listnew.Email, '')
or COALESCE(metroList.LocName, '') <> COALESCE(listnew.LocName, '')
) THEN
Of course, this assumes that you're fine with NULL meaning the same as an empty string (which may not be appropriate).
Take a look at this BOL article on NULL comparisons.
As I understand the question you are looking for an expression that emulates IS DISTINCT FROM.
The answer you have accepted is not correct then
WITH metroList([User])
AS (SELECT CAST(NULL AS VARCHAR(10))),
listnew([User])
AS (SELECT 'Foo')
SELECT *
FROM metroList
JOIN listnew
ON NULLIF(metroList.[User], listnew.[User]) IS NOT NULL
Returns zero rows. Despite the values under comparison being NULL and Foo.
I would use the technique from this article: Undocumented Query Plans: Equality Comparisons
WHEN MATCHED AND EXISTS (
SELECT metroList.[User], metroList.Email,metroList.LocName
EXCEPT
SELECT listnew.[User], listnew.Email,listnew.LocName
)

Informix: Select null problem

Using Informix, I've created a tempory table which I am trying to populate from a select statement. After this, I want to do an update, to populate more fields in the tempory table.
So I'm doing something like;
create temp table _results (group_ser int, item_ser int, restype char(4));
insert into _results (group_ser, item_ser)
select
group_ser, item_ser, null
from
sometable
But you can't select null.
For example;
select first 1 current from systables
works but
select first 1 null from systables
fails!
(Don't get me started on why I can't just do a SQL Server like "select current" with no table specified!)
You don't have to write a stored procedure; you simply have to tell IDS what type the NULL is. Assuming you are not using IDS 7.31 (which does not support any cast notation), you can write:
SELECT NULL::INTEGER FROM dual;
SELECT CAST(NULL AS INTEGER) FROM dual;
And, if you don't have dual as a table (you probably don't), you can do one of a few things:
CREATE SYNONYM dual FOR sysmaster:"informix".sysdual;
The 'sysdual' table was added relatively recently (IDS 11.10, IIRC), so if you are using an older version, it won't exist. The following works with any version of IDS - it's what I use.
-- #(#)$Id: dual.sql,v 2.1 2004/11/01 18:16:32 jleffler Exp $
-- Create table DUAL - structurally equivalent to Oracle's similarly named table.
-- It contains one row of data.
CREATE TABLE dual
(
dummy CHAR(1) DEFAULT 'x' NOT NULL CHECK (dummy = 'x') PRIMARY KEY
) EXTENT SIZE 8 NEXT SIZE 8;
INSERT INTO dual VALUES('x');
REVOKE ALL ON dual FROM PUBLIC;
GRANT SELECT ON dual TO PUBLIC;
Idiomatically, if you are going to SELECT from Systables to get a single row, you should include 'WHERE tabid = 1'; this is the entry for Systables itself, and if it is missing, the fact that your SELECT statement does return any data is the least of your troubles. (I've never seen that as an error, though.)
This page says the reason you can't do that is because "NULL" doesn't have a type. So, the workaround is to create a sproc that simply returns NULL in the type you want.
That sounds like a pretty bad solution to me though. Maybe you could create a variable in your script, set it to null, then select that variable instead? Something like this:
DEFINE dummy INT;
LET dummy = NULL;
SELECT group_ser, item_ser, dummy
FROM sometable
SELECT group_ser, item_ser, replace(null,null) as my_null_column
FROM sometable
or you can use nvl(null,null) to return a null for your select statement.
Is there any reason to go for an actual table? I have been using
select blah from table(set{1})
select blah from table(set{1})
is nice when you are using 10.x database. This statement doesn't touch database. The amount of read/write operations is equal to 0,
but
when you're using 11.x it will cost you at least 4500 buffer reads because this version of Informix creates this table in memory and executes query against it.
select to_date(null) from table;
This works when I want to get a date with null value
You can use this expression (''+1) on the SELECT list, instead of null keyword. It evaluates to NULL value of type DECIMAL(2,0).
This (''+1.0001) evaluates to DECIMAL(16,4). And so on.
If you want DATE type use DATE(''+1) to get null value of type DATE.
(''+1)||' ' evaluates to an empty string of type VARCHAR(1).
To obtain NULL value of type VARCHAR(1) use this expression:
DATE(''+1)||' '
Works in 9.x and 11.x.