Using the Continuation character in a DEFINE statement in Oracle SQL Developer - sql

I have the following code in which I'm using a variable to pass a list of values to multiple SQL statements (I can't save in a table as I don't have authority and don't want to have to maintain the list in all of the various SQL sections).
It works fine as long as all of the values are on a single line... but as I have so many values; I'd like to split it into multiple lines and use the Continuation Character '-'.
I'm running Oracle SQL Developer 2.1.1.64 against Oracle 10g (I also tried this in PL/SQL Developer and it failed there as well)
--=========================================
define subclasses = ('10-1010-10','10-1010-15','10-1010-20', -
'10-1010-25','10-1010-30') --- there are another 60 values...
select item from item_master where ((subclass) in &&subclasses);
Select Price from Item_prices where ((subclass) in &&subclasses);
--=========================================
I get the following error
ORA-01722: invalid number
01722. 00000 - "invalid number"
as it is parsing the code as
select item from item_master where ((subclass) in ('10-1010-10','10-1010-15',
'10-1010-20', -'10-1010-25','10-1010-30'))
...keeping the continuation code '-' in the SQL....tho it DOES go to the 2nd line of values.
If I remove the '-' ... it only processes the values on the first line and parses as
select item from item_master where ((subclass) in ('10-1010-10','10-1010-15','10-1010-20', )
... losing the second to nth line of values (and throwing errors as it ends w/ ',' and doesn't have the final ')'). How do I fix this?

You could do this:
column subc new_value subclasses
select q'[('10-1010-10','10-1010-15','10-1010-20',
'10-1010-25','10-1010-30')]' as subc
from dual;
Now &subclasses. will contain all the codes.
NB I used the q'[...]' quote syntax to avoid have to double up all the quotes in the data.

I noticed that you are trying to substitute a list of string variables into the select statement. You should rewrite your define statement to make it a single list of strings like this:
define subclasses = '''10-1010-10'',''10-1010-15'',''10-1010-20'', -
''10-1010-25'',''10-1010-30'''; --- there are another 60 values...
The - should be fine as a continuation character (see Oracle documentation here).
Now, when you execute your select statements you need to edit the WHERE clause so they are formatted so it will plug those values directly in there as written:
Select item from item_master where subclass in (&subclasses);
Select Price from Item_prices where subclass in (&subclasses);
This will end up being interpreted as if you had written:
Select item from item_master
where subclass in ('10-1010-10','10-1010-15','10-1010-20', '10-1010-25','10-1010-30');
If you have a lot of values though, you might run into limitations for substitution variables if you are using SQL*Plus (i.e. limited to 240 bytes per variable). In that case, you can either split the variables into multiple variables and concatenate them in the SELECT, or if you are in a PL/SQL environment, you can create variables that will hold the larger data size.

Related

Query to ignore rows which have non hex values within field

Initial situation
I have a relatively large table (ca. 0.7 Mio records) where an nvarchar field "MediaID" contains largely media IDs in proper hexadecimal notation (as they should).
Within my "sequential" query (each query depends on the output of the query before, this is all in pure T-SQL) I have to convert these hexadecimal values into decimal bigint values in order to do further calculations and filtering on these calculated values for the subsequent queries.
--> So far, no problem. The "sequential" query works fine.
Problem
Unfortunately, some of these Media IDs do contain non-hex characters - most probably because there was some typing errors by the people which have added them or through import errors from the previous business system.
Because of these non-hex chars, the whole query fails (of course) because the conversion hits an error.
For my current purpose, such rows must be skipped/ignored as they are clearly wrong and cannot be used (there are no medias / data carriers in use with the current business system which can have non-hex character IDs).
Manual editing of the data is not an option as there are too many errors and it is not clear with what the data must be replaced.
Challenge
To create a query which only returns records which have valid hex values within the media ID field.
(Unfortunately, my SQL skills are not enough to create the above query. Your help is highly appreciated.)
The relevant section of the larger query looks like this (xxxx is where your help comes in :-))
select
pureMediaID
, mediaID
, CUSTOMERID
,CONTRACT_CUSTOMERID
from
(
select concat('0x', Replace(Ltrim(Replace(mediaID, '0', ' ')), ' ', '0')) AS pureMediaID
--, CUSTOMERID
, *
from M_T_CONTRACT_CUSTOMERS
where mediaID is not null
and mediaID like '0%'
and xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
) as inner1
EDIT: As per request I have added here some good and some bad data:
Good:
4335463357
4335459809
1426427996
4335463509
4335515039
4335465134
4427370396
4335415661
4427369036
4335419089
004BB03433
004e7cf9c6
00BD23133
00EE13D8C1
00CCB5522C
00C46522C
00dbbe3433
Bad:
4564589+
AB6B8BFC.8
7B498DFCnm
DB218DFChb
d<tgfh8CFC
CB9E8AFCzj
B458DFCjhl
rytzju8DFC
BFCtdsjshj
DB9888FCgf
9BC08CFCyx
EB198DFCzj
4B628CFChj
7B2B8DFCgg
After I did upgrade the compatibility level of the SQL instance to SQL2016 (it was below 2012 before) I could use try_convert with same syntax as the original convert function as donPablo has pointed out. With that the query could run fully through and every MediaID which is not a correct hex value gets nicely converted into a null value - really, really nice.
Exactly what I needed.
Unfortunately, the solution of ALICE... didn't work out for me as this was also (strangely) returning records which had the "+" character within them.
Edit: The added comment of Alice... where you create a calculated field like this:
CASE WHEN "KEY" LIKE '%[^0-9A-F]%' THEN 0 ELSE 1 end as xyz
and then filter in the next query like this:
where xyz = 1
works also with SQL Instances with compatibility level < SQL 2012.
Great addition for people which still have to work with older SQL instances.
An option (although not ideal in terms of performance) is to check the characters in the MediaID through a case statement and regular expression
Hexadecimals cannot contain characters other than A-F and numbers between 0 and 9
CASE WHEN MediaID LIKE '%[0-9A-F]%' THEN 1 ELSE 0 END
I would recommend writing a function that can be used to evaluate MediaID first and checks if it is hexadecimal and then running the query for conversion

Why do I get different results depending on the function I use? (SQL Server)

I've been tasked with creating a report for my company. The report is generated from the results returned by the Stored Procedure spGenerateReport, which has multiple filters.
Inside the SP, this is how the filter is expected to work:
SELECT * FROM MyTable WHERE column1 IN (
'filters', 'for', 'this', 'report'
)
Entering the code above yields ~30000 rows in 9s. However, I want to be able to change my SP's filter by passing it a single argument (since I may use 1 or 2 or n filters), like so:
spGenerateReport 'Filters,for,this,report'
For this I have the User-Created Function fnSplitString (yes, I do know that there is a STRING_SPLIT function but I can't use it due to a lower compatibility level of my database) which splits a single string into a table, like so:
SELECT splitData FROM fnSplitString('Filters,for,this,report')
Returns:
splitData
------
Filters
for
this
report
Thus the final code in my SP is:
SELECT * FROM MyTable WHERE column1 IN (
SELECT * FROM fnSplitString('Filters,for,this,report')
)
However, this instead yields ~10000 rows in 60s. The time taken to complete this SP is weird but isn't too much of a problem, however nearly a quarter of my rows disappearing into the void certainly is. The results only have rows from the first couple filters (for example, 'Filters' and 'for'; if I change the order of the arguments (e.g.: fnSplitString('report,for,Filters,this')), I get a different number of rows, and only from filters 'report', 'for', 'Filters'! I don't understand why using the function returns different results than those obtained when using the literal strings. Is there some inside gimmick that I'm not aware of?
PS - I'm sorry in advance for being bad at explaining myself, and for any grammar mistakes
You should definitely be getting the same results with both techniques. Something is wrong.
You havent posted the fnSplitString code but I suspect fnSplitString is not outputting the last string in the list, or maybe the last string in the list is being truncated before it reaches fnSplitString so that no matches are found.
e.g. if the parameter going into your spGenerateReport stored procedure is varchar(20) then what will reach the function is 'Filters,for,this,rep' with the last bit truncated.
SSRS, for example, will truncate strings that are being passed into an SP instead of warning you with an error message

Randomly insert 1 of 3 declared variables

I have three variables that are declared and have an integer value assigned to them.
I am trying to randomly assign the integer value to a field in an UPDATE statement, but get an error.
This is statement I am trying to execute:
FOR user_record IN (SELECT * FROM users_to_add) LOOP
UPDATE
customer."user"
SET
primary_site_id = ({site_GRO, site_WHS, site_SHR}[])[ceil(random()*3)],
WHERE
userid = (SELECT userID FROM customer.user
WHERE emailaddress=user_record.email_address);
END LOOP;
I am getting:
SyntaxError: syntax error at or near "{"
This same format works if the value being randomly selected is a string but since these are variables, the inside curly brackets can't be enclosed in quotes.
Use an ARRAY constructor instead of the (invalid) array literal.
(ARRAY[site_GRO, site_WHS, site_SHR])[ceil(random()*3)]
However, a set-based solution is typically more efficient than looping:
UPDATE customer."user" u
SET primary_site_id = CASE trunc(random()*3)::int
WHEN 0 THEN site_gro -- your variables here
WHEN 1 THEN site_whs
WHEN 2 THEN site_shr
END
FROM users_to_add ua
WHERE u.userid = ua.email_address;
Should achieve the same. Works inside a PL/pgSQL block or as standalone SQL DML command (then you need to interpolate variable values yourself).
A single multi-row UPDATE is much cheaper than many updates in a loop.
trunc() is slightly more correct than ceil(), as random() returns a value in the domain [0,1) (1 excluded). It's also faster.
And a CASE construct is substantially faster than building an array just to extract a single element from it.
Asides:
Avoid reserved words like user as identifiers. Always requires double-quoting, and can lead to confusing errors when forgotten.
Also avoid random capitalization in identifiers. This goes for SQL as well as for PL/pgSQL. See:
Are PostgreSQL column names case-sensitive?
Perhaps you can try splitting the index and array out into their own vars?
FOR user_record IN (SELECT * FROM users_to_add) LOOP
a := ARRAY[site_GRO, site_WHS, site_SHR];
i := ceil(random()*3);
UPDATE
customer."user"
SET
primary_site_id = a[i]
WHERE
userid = (SELECT userID FROM customer.user WHERE emailaddress=user_record.email_address);
END LOOP;

Writing a query that will handle three different labels

I have 3 different drop down options. If I select one option at a time it works, but on selecting more than one it will throw an error. The three labels refer to three different versions. Everything works but when I select more than one option in the drop down it crashes. Need help understanding it why
The three parameters are
Select all:
Report1
Report2
Report3
Here my query:
SELECT
ServerInfo.Version,
ServerInfo.Type,
ProjInfo.ProjName,
ServerInfo.ServName
FROM
ProjInfo, ServerInfo
WHERE ServerInfo.Version LIKE('%'+#ServerReport+'%')
Incorrect syntax near ','.
Query execution failed for dataset 'Report'. (rsErrorExecutingCommand)
An error has occurred during report processing. (rsProcessingAborted)
The problem here is that the query is expecting a single string value, but the report is sending it an array of values when there are multiples selected.
In the parameter tab of your dataset properties, update the expression like this:
="," & Join(Parameters!ServerReport.Value, ",") & ","
This will combine the selected values into a single, comma-separated string.
Update your WHERE clause like this:
#ServerReport like '%,' + ServerInfo.Version + ',%'
This allows it to scan for the version string in the parameter string. The commas on the outside of both the joined parameter and the like statement prevent it from inadvertently matching partial strings.
Clarification:
This is all assuming that you actually need the like statement in the first place. Normally you would just say:
ServerInfo.Version IN (#ServerReport)
This would accept multiple values without any other changes. But the strings would have to be an exact match.

SQL query ignores "not between"

I have a data source like following
If I ran the following sql query it removes all the records with "Seg Type" MOD and ignores the Fnn range given.
select * from NpsoQueue
where SegmentType not in ('MOD')
and Fnn not between 0888452158 and 0888452158
I want the query to consider both conditions. So, if I ran the query it should remove only the first record
The logic in your where clause is incorrect
Use
select * from NpsoQueue
where NOT (
SegmentType = 'MOD'
and Fnn between '0888452158' and '0888452158'
)
Also, a number with a leading zero is a string literal so you need to put single quotes around it to preserve the leading zero and stop implicit casts happening
As mentioned by #TriV you could also use OR. These are fundamental boolean logic concepts, i.e. not related to SQL Server or databases