Oracle order by query using select case - sql

in Oracle Live SQL i was trying to use simple order by sql using select (case when) query
i tried to get to same result select * from tt order by 1
replace 1 with (select (case when 1=1 then 1 else 2 end) from dual)
but two result completely different.
i want table ordered by column 1 however the query using select case when query doesn't sort by column 1.
I don't know why and want to know how this query works in oracle db

Compare
...
order by 2
and
...
order by 1+1
At "compile" time the first 2 is an integer constant so it is a position of the column, the db engine sorts by the specified column. The second 1+1 is an integer expression and the db engine sorts by this value '2'. Same, (select (case when 1=1 then 1 else 2 end) from dual) is an expression, not a column specification.

When you specify a number in the ORDER BY clause, Oracle will sort by that column of the resulting select. As an example, ORDER BY 1,2 will sort by the first column, then the second column. If there is no second column, then you will get an error.
In the ORDER BY of the outermost query, there is essentially no sorting happening in your query because 1 is always returned from your subquery. This is sorting by the value 1 and not the first column.
If you explain the logic you are hoping to achieve, then we may be able to assist, but that is what is happening with your existing queries.

Related

SQL Server - Pagination Without Order By Clause

My situation is that a SQL statement which is not predictable, is given to the program and I need to do pagination on top of it. The final SQL statement would be similar to the following one:
SELECT * FROM (*Given SQL Statement*) b
OFFSET 0 ROWS FETCH NEXT 50 ROWS ONLY;
The problem here is that the *Given SQL Statement* is unpredictable. It may or may not contain order by clause. I am not able to change the query result of this SQL Statement and I need to do pagination on it.
I searched for solution on the Internet, but all of them suggested to use an arbitrary column, like primary key, in order by clause. But it will change the original order.
The short answer is that it can't be done, or at least can't be done properly.
The problem is that SQL Server (or any RDBMS) does not and can not guarantee the order of the records returned from a query without an order by clause.
This means that you can't use paging on such queries.
Further more, if you use an order by clause on a column that appears multiple times in your resultset, the order of the result set is still not guaranteed inside groups of values in said column - quick example:
;WITH cte (a, b)
AS
(
SELECT 1, 'a'
UNION ALL
SELECT 1, 'b'
UNION ALL
SELECT 2, 'a'
UNION ALL
SELECT 2, 'b'
)
SELECT *
FROM cte
ORDER BY a
Both result sets are valid, and you can't know in advance what will you get:
a b
-----
1 b
1 a
2 b
2 a
a b
-----
1 a
1 b
2 a
2 b
(and of course, you might get other sorts)
The problem here is that the *Given SQL Statement" is unpredictable. It may or may not contain order by clause.
your inner query(unpredictable sql statement) should not contain order by,even if it contains,order is not guaranteed.
To get guaranteed order,you have to order by some column.for the results to be deterministic,the ordered column/columns should be unique
Please note: what I'm about to suggest is probably horribly inefficient and should really only be used to help you go back to the project leader and tell them that pagination of an unordered query should not be done. Having said that...
From your comments you say you are able to change the SQL statement before it is executed.
You could write the results of the original query to a temporary table, adding row count field to be used for subsequent pagination ordering.
Therefore any original ordering is preserved and you can now paginate.
But of course the reason for needing pagination in the first place is to avoid sending large amounts of data to the client application. Although this does prevent that, you will still be copying data to a temp table which, depending on the row size and count, could be very slow.
You also have the problem that the page size is coming from the client as part of the SQL statement. Parsing the statement to pick that out could be tricky.
As other notified using anyway without using a sorted query will not be safe, But as you know about it and search about it, I can suggest using a query like this (But not recommended as a good way)
;with cte as (
select *,
row_number() over (order by (select 0)) rn
from (
-- Your query
) t
)
select *
from cte
where rn between (#pageNumber-1)*#pageSize+1 and #pageNumber*#pageSize
[SQL Fiddle Demo]
I finally found a simple way to do it without any order by on a specific column:
declare #start AS INTEGER = 1, #count AS INTEGER = 5;
select * from (SELECT *,ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS fakeCounter
FROM (select * from mytable) AS t) AS t2 order by fakeCounter OFFSET #start ROWS
FETCH NEXT #count ROWS ONLY
where select * from mytable can be any query

Condition inside a count -SQL

I am trying to write a condition inside a count statement where it should only count the entries which do not have an ENDDATE. i am looking for writing the condition inside the count as this is a very small part of a large SQl Query
sample query,
select product, count(*) as quantity
from table
where end_date is null
group by age
This query lists quantity for each product which do not have an end date
One method uses conditional aggregation:
select sum(case when end_date is null then 1 else 0 end) as NumNull
. . .
Another method is just to subtract two counts:
select ( count(*) - count(end_date) ) as NumNull
count(end_date) counts the number that are not NULL, so subtracting this from the full count gets the number that are NULL.
Uhmmmm.
It sounds like you are looking for conditional aggregation.
So, if you have a current statement that's sort of working (and we're just guessing because we don't see anything you have attempted so far...)
SELECT COUNT(1)
FROM mytable t
And you want another another expression that returns a count of rows that meet some set of conditions...
and when you say "do not have an ENDDATE", you are refderring to rows that have an ENDDATE value of NULL (and again, we're just guessing that the table has a column named ENDDATE. Every row will have an ENDDATE column.)
We'll use a ANSI standards compliant CASE expression, because this would work in most databases (SQL Server, Oracle, MySQL, Postgres... and we don't have clue what database you are using.
SELECT COUNT(1)
, COUNT(CASE WHEN t.ENDDATE IS NULL THEN 1 ELSE NULL END) AS cnt_null_enddate
FROM mytable t

getting same top 1 result in sql server

I have this query:
SELECT
IT_approvaldate
FROM
t_item
WHERE
IT_certID_fk_ind = (SELECT DISTINCT TOP 1 IT_certID_fk_ind
FROM t_item
WHERE IT_rfileID_fk = '4876')
ORDER BY
IT_typesort
Result when running this query:
I need get top 1 result. (2013-04-27 00:00:00) problem is when I select top 1, getting 2nd result.
I believe reason for that order by column value same in those two result.
please see below,
However I need get only IT_approvaldate column top 1 as result of my query.
How can I do this? Can anyone help me to solve this?
Hi use below query and check
SELECT IT_approvaldate FROM t_item WHERE IT_certID_fk_ind =(SELECT DISTINCT top 1 IT_certID_fk_ind FROM t_item WHERE IT_rfileID_fk ='4876' ) and IT_approvaldate is not null ORDER BY IT_typesort
This will remove null values from the result
If you want NULL to be the last value in the sorted list you can use ISNULL in ORDER BY clause to replace NULL by MAX value of DATETIME
Below code might help:
SELECT TOP 1 IT_approvaldate
FROM t_item
WHERE IT_certID_fk_ind = (SELECT DISTINCT top 1 IT_certID_fk_ind FROM t_item WHERE IT_rfileID_fk ='4876' )
ORDER BY IT_typesort ASC, ISNULL(IT_approvaldate,'12-31-9999 23:59:59') ASC;
TSQL Select queries are not inherently deterministic. You must add a tie-breaker or by another row that is not.
The theory is SQL Server will not presume that the NULL value is greater or lesser than your row, and because your select statement is not logically implemented until after your HAVING clause, the order depends on how the database is setup.
Understand that SQL Server may not necessarily choose the same path twice unless it thinks it is absolutely better. This is the reason for the ORDER BY clause, which will treat NULLs consistently (assuming there is a unique grouping).
UPDATE:
It seemed a good idea to add a link to MSDN's documentation on the ORDER BY. Truly, it is good practice to start from the Standard/MSDN. ORDER BY Clause - MSDN

Conditional ORDER BY depending on column values

I need to write a query that does this:
SELECT TOP 1
FROM a list of tables (Joins, etc)
ORDER BY Column X, Column Y, Column Z
If ColumnX is NOT NULL, then at the moment, I reselect, using a slightly different ORDER BY.
So, I do the same query, twice. If the first one has a NULL in a certain column, I return that row from my procedure. However, if the value isn't NULL - I have to do another identical select, except, order by a different column or two.
What I do now is select it into a temp table the first time. Then check the value of the column. If it's OK, return the temp table, else, redo the select and return that result set.
More details:
In english, the question I am asking the database:
Return my all the results for certain court appearance (By indexed foreign key). I expect around 1000 rows. Order it by the date of the appearance (column, not indexed, nullable), last appearance first. Check an 'importId'. If the import ID is not NULL for that top 1 row, then we need to run the same query - but this time, order by the Import ID (Last one first), and return that row. Or else, just return the top 1 row from the original query.
I'd say the BEST way to do this is in a single query is a CASE statement...
SELECT TOP 1 FROM ... ORDER BY
(CASE WHEN column1 IS NULL THEN column2 ELSE column1 END)
You could use a COALESCE function to turn nullable columns into orderby friendly values.
SELECT CAST(COALESCE(MyColumn, 0) AS money) AS Column1
FROM MyTable
ORDER BY Column1;
I used in Firebird (columns are numeric):
ORDER BY CASE <condition> WHEN <value> THEN <column1>*1000 + <column2> ELSE <column3>*1000 + <column4> END

SQL to have one specific record at the top, all others below

I am trying to put together a query that will display one specific record (found by the record's primary ID) at the top, and display all other records below it, sorted by date (I have "date_added" as one of the fields in the table, in addition to primary ID).
I could do this with a UNION (first select would locate the record I want, and the other select would display all other records), but I'm wondering if is there perhaps a better way?
I'm using Oracle, by the way.
You can do this by sorting by two fields
The first would be an expression that returns 0 if the row is the one you want or 1 if it isn't. Sort will be ascending so you get your preferred record first.
The second sort field would be date_added so the remaining records are sorted in this order.
Afraid I don't know oracle by in sql server it would be something like
select *
from the_table
order by (case id when 999 then 0 else 1 end), date_added desc
An easier way would be a fancy order by construct. Here's an example for pk = 123:
select *
from YourTable
order by case when yourpk = 123 then 1 else 2 end, date_added
I don't know Oracle exactly, but you could perhaps do something like..
ORDER BY IF(id == THE_ID, 0, 1), date_added
You can sort more than one record to the top using the same technique
999 first
998 second
followed by everything else sorted by date
select *
from the_table
order by (case id when 999 then 0 when 998 then 1 else 2 end), date_added desc
SELECT *
FROM `Table`
ORDER BY (`id` = THE_ID) DESC, `date_added` DESC
The simple way would be to recognise that you want to display two separate things and therefore write to separate straightforward queries. One query to retrieve the first record, and the second to retrieve the sorted list. There is no real performance advantage to doing anything more than this because of one unique record.