SQL Order By while ignoring leading characters - sql

Im looking for some help with regards to ordering by a numeric value but the numeric values have leading chars (-).
For example
Column Order #
---5
--8
-6
A simple ORDER BY Order# DESC gives me an unordered output. How do you ignore the leading chars when sorting data such as the Order # above?

Using the H2 database syntax and functions, and a table declared as:
create table foo(bar varchar(10));
with the rows:
'---2'
'--3'
'-1'
the following query:
select bar from foo
order by cast (replace(bar, '-') as int);
gives me the results:
BAR
-1
---2
--3
which seems to be what you're after.
Disclaimer: this is possibly not the best way to do this, since the computed value is not indexed. For just ordering a reasonably-sized result set it doesn't matter.

The solution I came up with is similar to the answer given,
I can just cast the column data to an int and sort it the way ie
ORDER BY CAST((ColumnName) AS INT) DESC;
this sucessfully ignores the leading chars and sorts it by the numbers

Related

Get the greatest value in a nvarchar column

I'm developing a database with SQL Server 2012 SP2.
I have a table with a NVARCHAR(20) column, and it will have numbers: "000001", "000002", etc.
I need to get the greatest value in that column and convert it to int. How can I do it?
I have found that I can convert a nvarchar to int with this sql sentence:
SELECT CAST(YourVarcharCol AS INT) FROM Table
But I don't know how can I get the max value in that column because the numbers are nvarchar.
UPDATE:
By the way, this column is NVARCHAR because I need to store text on it. I'm testing my solution and I need to store ONLY numbers to test it.
If your numbers are padded like in the example given and all have the same width, you can just sort them alphanumerically and then cast the max-value to INT or BIGINT (depending on your numbers range).
If there are very many rows it was much faster, especially if there is an index on this column...
Something like
SELECT TOP 1 * FROM YourTable
ORDER BY NumberColumn DESC
or, if you need the max-value only:
SELECT MAX(NumberColumn) FROM YourTable
If you have to deal with negative numbers or differently padded numbers you have to cast them first
SELECT TOP 1 * FROM YourTable
ORDER BY CAST(NumberColumn AS INT) DESC
or
SELECT MAX(CAST(NumberColumn AS INT)) FROM YourTable
Please note:
If you've got very many rows, the second might get rather slow. Read about sargable
If your NumberColumn might include invalid values, you have to check, Read about ISNUMERIC().
The best solution - in any case - was to use an indexed numeric column to store these values
Try this one...
I think MAX is enough.
SELECT max(CAST(YourVarcharCol AS INT)) FROM Table
Try this
SELECT MAX(t.Y) from (SELECT CAST(YourVarcharCol AS INT) as Y FROM Table) t
You should try this on finding the highest value:
SELECT MAX(CAST(YourVarcharCol AS INT)) AS FROM Table
If all the data follow the same padding and formatting pattern, a simple max(col) would do.
However, if not, you have to cast the values to int first. Searching on a columns cast to some other datatype will not use an index, if there's any, but will scan the whole table instead. It may or may not be OK for you, depending on requirements and number of rows in the table. If performance is what you need, then create a calculated column as try_cast( col as int), and create an index on it.
Note that you should not use cast, but try_cast instead, to guard against values that can't be cast (if you use a datatype to store something which is essencially of another datatype, it always opens up a possibility for errors).
Of couse, if you can change the original column's type to int, it would be the best.
This will return max int value
SELECT MAX(IIF(ISNUMERIC(YourVarcharCol) = 1, YourVarcharCol, '0') * 1) FROM Table
You can use like this
select max(cast(ColumnName AS INT ))from TableName

Oracle numeric string column and indexing

I have a numeric string column in oracle with or without leading zeros samples:
00000000056
5755
0123938784579343
00000000333
984454
The issue is that partial Search operations using like are very slow
select account_number from accounts where account_number like %57%
one solution is to restrict the search to exact match only and add an additional integer column that will represent the numeric value for exact matches.
At this point I am not sure we can add an additional column,
Do you guys have any other ideas?
Is it possible to tell Oracle to index the numeric string column as an integer value so we can do exact numeric match on it?
for example query on value :00000000333
will be:
select account_number from accounts where account_number = '333'
While ignoring the leading zeros.
I can use regex_like and ignore them, but I am afraid its going to be slow.
Oracle supports function based indexes. SO if you index the result of to_number:
CREATE INDEX acocunt_number_idx
ON accounts(TO_NUMBER(account_number))
The way, if you use a query with to_number for the exact numeric value, the index will be used:
SELECT account_number
FROM accounts
WHERE TO_NUMBER(account_number) = 333
You can use to_number.
select account_number from accounts
where to_number(account_number) = 333

Combining concatenation with ORDER BY

I have troubles in combining concatenation with order by in Postgre (9.1.9).
Let's say, I have a table borders with 3 fields:
Table "borders"
Column | Type | Modifiers
---------------+----------------------+-----------
country1 | character varying(4) | not null
country2 | character varying(4) | not null
length | numeric |
The first two fields are codes of the countries and the third one is the length of the border among those countries.
The primary key is defined on the first two fields.
I need to compose a select of a column that would have unique values for the whole table, in addition this column should be selected in decreasing order.
For this I concatenate the key fields with a separator character, otherwise two different rows might give same result, like (AB, C and A, BC).
So I run the following query:
select country1||'_'||country2 from borders order by 1;
However in the result I see that the '_' character is omited from the sorting.
The results looks like this:
?column?
----------
A_CH
A_CZ
A_D
AFG_IR
AFG_PK
AFG_TAD
AFG_TJ
AFG_TM
AFG_UZB
A_FL
A_H
A_I
.
.
You can see that the result is sorted as if '_' doesn't exists in the strings.
If I use a letter (say 'x') as a separator - the order is correct. But I must use some special character that doesn't appear in the country1 and country2 fields, to avoid contentions.
What should I do, in order to make the '_' character to be taken into account during the sorting.
EDIT
It turned out that the concatenation has nothing to do with the problem. The problem is that the order by simply ignores '_' character.
select country1 || '_' || country2 collate "C" as a
from borders
order by 1
sql fiddle demo
Notes according to discussion in comments:
1.) COLLATE "C" applies in the ORDER BY clause as long as it references the expression in the SELECT clause by positional parameter or alias. If you repeat the expression in ORDER BY you also need to repeat the COLLATE clause if you want to affect the sort order accordingly.
sql fiddle demo
2.) In collations where _ does not influence the sort order, it is more efficient to use fog's query, even more so because that one makes use of the existing index (primary key is defined on the first two fields).
However, if _ has an influence, one needs to sort on the combined expression:
sql fiddle demo
Query performance (tested in Postgres 9.2):
sql fiddle demo
PostgreSQL Collation Support in the manual.
Just order by the two columns:
SELECT country1||'_'||country2 FROM borders ORDER BY country1, country2;
Unless you use aggregates or windows, PostgreSQL allows to order by columns even if you don't include them in the SELECT list.
As suggested in another answer you can also change the collation of the combined column but, if you can, sorting on plain columns is faster, especially if you have an index on them.
What happens when you do the following?
select country1||'_'||country2 from borders order by country1||'_'||country2
My knowledge on order by 1 only does an ordinal sort. It won't do anything on concatenated columns. Granted, I'm speaking from SQL Server knowledge, so let me know if I'm way off base.
Edited: Ok; just saw Parado's post as I posted mine. Maybe you could create a view from this query (give it a column name) and then requery the view, order by that column? Or do the following:
select country_group from (
select country1||'_'||country2 as country_group from borders
) a
order by country_group

SQL Framing Query Problem

I am running a SQL query that only returns a portion of the entire data. In other words, the data is 'framed.' I have an 'ORDER(ORDER BY' part and for whatever reason, that part isn't working all of the time. Sometimes I get what I expect, and other times I don't.
The query:
SELECT
*
FROM
(
SELECT
ROW_NUMBER() OVER(ORDER BY [datetime] DESC, timeMicroSec DESC) AS rowNum,
...
FROM
...
WHERE
...
) AS TempTbl
WHERE
rowNum BETWEEN #startRow AND #endRow;
The whole data query works when it is not framed and I use an 'ORDER BY' clause at the end. In the image below, the [datetime] column and the [timeMicroSec] column are joined with string concatenation.
As you can see, the ordering is all messed up. Any help would be appreciated.
When you cast a DateTime as a Varchar, it changes the way that SQL Server will order the column. It will no longer order it chronologically, but instead as just a plain old string.
If the data type is a DateTime, you would get the following descending sort order:
01/11/2011
02/22/2010
The first date is later chronologically... but if the data type is a Varchar... it would be sorted as:
02/22/2010
01/11/2011
Because the string "02" comes after "01"... the actual date value doesn't matter at this point. When you concatenate your date with timeMicroSec, you change the sorting to a Varchar sort.
Like the others said... if you order by RowNum instead of by your concatenated string, you will get a chronological order.
ORDER BY rowNum
as the last part of your query will fix your problem.

Problem with MySQL ORDER BY - can I use natsort to fix it?

I need to use natural sorting with a MySQL result since it currently sorts my INT values as follows:
1
123
1256
22
231
[etc]
While I would need it to sort like
1
22
231
1256
I know of the "natsort" function, but it does not work for a MySQL query result. How can I sort (naturally), is it possible to do this within the query?
Thanks for the help!
EDIT: Example query $result = mysql_query("SELECT * FROM forums ORDER BY 'posts' DESC;");
Not entirely sure if my use of DESC there is valid, but it doesn't throw an error so it must be fine. Unfortunately, it seems changing DESC to ASC also has no effect...
The query you've posted there is sorting by a constant string expression, rather than a column name or position. Either use backticks to quote, or take out the quotes:
SELECT * FROM forums ORDER BY `posts` DESC
or maybe
SELECT * FROM forums ORDER BY posts DESC
Would explain why changing between ASC and DESC has no effect.
Initial Answer Was:
The sort order you describe suggests the INT values are actually being stored as a character type (or maybe converted to character before sorting).
Check whether you are storing the data in a numeric or character type. If possible use a numeric type, then the natural sort order will be as you require. If you can't change the underlying data type to be INT, then you can do this in your query (e.g. using CAST), probably at cost of performance.
If posts was a non-INT column, you could try this:
SELECT * FROM forums ORDER BY CAST(`posts` AS INT) DESC
Also have a look at http://drupal.org/project/natsort. This is a Drupal module that can be used to facilitate MySQL sorting.