I am trying to convert the Oracle script to SQL Server script. But it seems my MIN() function is not working as expected as it is being executed in Oracle.
For SQL Server :
SELECT MIN(v)
FROM (VALUES ('20013E17587A1_2'), ('20013E17587_2')) AS value(v);
Result: 20013E17587_2
However,
For ORACLE :
SELECT MIN(t.value)
FROM tab t;
Result: 20013E17587A1_2
I am getting this as a result. Can somebody explain why is this difference and what can be done to have same result?
Different sort rules. You're asking the database whether it considers _ to come before or after A. By default, Oracle uses a binary sort (and the code point 65 belonging to A is less than code point 95 belonging to _) while SQL Server uses the default collation of the database, which will be a linguistic ordering where _ is considered to precede any letter. If you want SQL Server to exhibit identical behavior, use something like
SELECT MIN(v COLLATE Latin1_General_BIN2)
FROM (VALUES ('20013E17587A1_2'), ('20013E17587_2')) AS value(v);
Actual correct placement of the COLLATE depends on your "real" query, which I presume this isn't -- you may want to change the collation of the column itself in the CREATE TABLE, for example.
Because Oracle looks for the ASCII value for each character during the comparison of each respective character ordered within the string while performing alphanumeric sorting. This is called binary sort which's default for Oracle DB.
ASCII('A') equals 65, and ASCII('_') equals 95. If the string was 20013E17587.2 instead of 20013E17587_2, then you'd get 20013E17587.2 as result, since ASCII('.') equals 46 which is less than 65.
Related
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
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
I am trying to connect a Filemaker DB to Firebird SQL DB in both ways import to FM and export back to Firebird DB.
So far it works using the MBS Plug-in but FM 13 Pro canot handle NULL.
That means that for example Timestamp fields that are empty (NULL) produce a "0" value.
Thats means in Time something like 01.01.1889 00:00:00.
So my idea was to simply ignore fields containing NULL.
But here my poor knowlege stops.
First I thought I can do this with WHERE, but this is ignoring whole records sets:
SELECT * FROM TABLE WHERE FIELD IS NOT NULL
Also I tried to filter it later on like this:
If (IsEmpty (MBS("SQL.GetFieldAsDateTime"; $command; "FIELD") ) = 0 ; MBS("SQL.GetFieldAsDateTime"; $command; "FIELD"))
With no result either.
This is a direct answer to halfbit's suggestion, which is correct but not for this SQL dialect. In a query to provide a replacement value when a field is NULL you need to use COALESCE(x,y). Where if X is null, Y will be used, and if Y is null then the field is NULL. Thats why it is common for me to use it like COALESCE(table.field,'') such that a constant is always outputted if table.field happens to be NULL.
select COALESCE(null,'Hello') as stackoverflow from rdb$database
You can use COALESCE() for more than two arguments, I just used two for conciseness.
I dont know the special SQL dialect, but
SELECT field1, field2, value(field, 0), ...FROM TABLE
should help you:
value gives the first argument, ie, your field if it is NOT NULL or the second argument if it is.
I need to order a select query using a varchar column, using numerical and text order. The query will be done in a java program, using jdbc over postgresql.
If I use ORDER BY in the select clause I obtain:
1
11
2
abc
However, I need to obtain:
1
2
11
abc
The problem is that the column can also contain text.
This question is similar (but targeted for SQL Server):
How do I sort a VARCHAR column in SQL server that contains words and numbers?
However, the solution proposed did not work with PostgreSQL.
Thanks in advance, regards,
I had the same problem and the following code solves it:
SELECT ...
FROM table
order by
CASE WHEN column < 'A'
THEN lpad(column, size, '0')
ELSE column
END;
The size var is the length of the varchar column, e.g 255 for varying(255).
You can use regular expression to do this kind of thing:
select THECOL from ...
order by
case
when substring(THECOL from '^\d+$') is null then 9999
else cast(THECOL as integer)
end,
THECOL
First you use regular expression to detect whether the content of the column is a number or not. In this case I use '^\d+$' but you can modify it to suit the situation.
If the regexp doesn't match, return a big number so this row will fall to the bottom of the order.
If the regexp matches, convert the string to number and then sort on that.
After this, sort regularly with the column.
I'm not aware of any database having a "natural sort", like some know to exist in PHP. All I've found is various functions:
Natural order sort in Postgres
Comment in the PostgreSQL ORDER BY documentation
I found in MYSQL and apparently other database engines that there is a "greatest" function that can be used like: greatest(1, 2, 3, 4), and it would return 4. I need this, but I am using IBM's DB2. Does anybody know of such an equivalent function, even if it only accepts 2 parameters?
I found somewhere that MAX should do it, but it doesn't work... it only works on selecting the MAX of a column.
If there is no such function, does anybody have an idea what a stored procedure to do this might look like? (I have no stored procedure experience, so I have no clue what DB2 would be capable of).
Why does MAX not work for you?
select max(1,2,8,3,1,7) from sysibm.sysdummy1
gives me
1
---------------
8
1 record(s) selected.
As Dave points out, MAX should work as it's overloaded as both a scalar and a column function (the scalar takes 2 or more arguments). This is the case in DB2 for LUW, DB2 for z/OS and DB2 for i5/OS. What exact version and platform of DB2 are you using, and what is the exact statement you are using? One of the requirements of the scalar version of MAX is that all the arguments are "compatible" - I suspect there may be a subtle type difference in one or more of the arguments you're passing to the function.
On Linux V9.1, the "select max (1,2,3) ..." gives -
SQL0440N No authorized routine named "MAX" of type "FUNCTION" having
compatible arguments was found. SQLSTATE=42884
It is a scalar function requiring either a single value or a single column name. On z/os, it behaves differently.
However, It does work as expected on Linux 9.5.
Two options:
What about sorting the column in descending and grabbing the top 1 row?
According to my "SQL Pocket Guide", MAX(x) returns the greatest value in a set.
UPDATE: Apparently #1 won't work if you are looking at columns.
It sounds crazy, but no such function exists in DB2, at least not in version 9.1. If you want to select the greater of two columns, it would be best to use a case expression.
You can also define your own max function. For example:
create function importgenius.max2(x double, y double)
returns double
language sql
contains sql
deterministic
no external action
begin atomic
if y is null or x >= y then return x;
else return y;
end if;
end
Defining the inputs and outputs as doubles lets you take advantage of type promotion, so this function will also work for integers. The "deterministic" and "no external action" statements help the database engine optimize use of the function.
If you want another max function to work for character inputs, you'll have to give it another name.
Please check with following query:
select * from table1 a,
(select appno as sub_appno,max(sno) as sub_maxsno from table1 group by appno) as tab2
where a.appno =tab2.sub_appno and a.sno=tab2.sub_maxsno