order by sql data using condtions (case) based on derived columns - sql

I want to put a condtion to sort SQL data based on the value of derived columns as follows:
SELECT DISTINCT sp.ID
, sp.Status
, sp.Rank
, sp.Price
, sp.SalePrice
, sp.Width
, sp.Height
, sp.QOH
, (sp.SalePrice*sp.QOH) As 'sp.Value'
, (sp.Price*sp.QOH) As 'sp.StandardValue'
FROM table
WHERE -- Conditions
ORDER BY
CASE WHEN 'sp.SalePrice' > 0 THEN 'sp.Value' END DESC,
CASE WHEN 'sp.SalePrice' = 0 THEN 'sp.StandardValue' END DESC
Gves this error:
Msg 145, Level 15, State 1, Line 1
ORDER BY items must appear in the select list if SELECT DISTINCT is specified.
if i try
ORDER BY
CASE WHEN sp.SalePrice > 0 THEN (sp.SalePrice*sp.QOH) As "sp.Value" END DESC,
CASE WHEN sp.SalePrice = 0 THEN (sp.Price*sp.QOH) As sp.StandardValue" END DESC
Gives error:
Incorrect syntax near the keyword 'As'.
it starts giving the same select distinct error if i try to remove aliases * as from order by cluase & only leave the multiplication part

I'm going to add a bunch of suggestions about general conventions below, but as to the main problem consider what you're trying to do with this example:
My_Table:
id value
1 1
2 2
3 1
Now, try to show distinct value ordering by id. Which should it be?
value
1
2
or
value
2
1
So, you're asking SQL Server to do something that may be impossible or at least unclear.
Now, as to conventions... Maybe some of this is just how you've posted here, but I would make the following suggestions for your code:
Avoid using reserved and SQL language words for names. This would include table, rank, and status.
Avoid using special characters in names. This would include sp.value. Sure, you can do it with a quoted identifier, but some front-ends, etc. might not support them even if SQL does and you don't really buy anything by using them in most cases.
Use the quoted identifier when you have to quote names. If you absolutely must violate one of the above two suggestions, use the standard quoted identifiers for SQL Server, which are [ and ]. If you want to quote aliases, use these as well (and you shouldn't have to typically quote aliases BTW). This helps to avoid the problem that Mark B points out.
Your CASE statement can be better written as one ordering column. Also, you should include an ELSE in most cases to avoid unhandled conditions. This may not be needed here as long as you can't have NULLs or negative values in any of the involved columns.
CASE
WHEN sp.SalePrice > 0 THEN (sp.SalePrice*sp.QOH)
WHEN sp.SalePrice = 0 THEN (sp.Price*sp.QOH)
END
I would personally avoid using the table alias (which it looks like you accidentally left out of your query) as part of your column aliases. It makes it much more confusing IMO because it makes it look like that aliased column is actually a column in the table.

Your second example is closest as it specifies the columns correctly. However, you cannot alias columns in the order by. This would work:
ORDER BY
CASE WHEN sp.SalePrice > 0 THEN sp.SalePrice*sp.QOH END DESC,
CASE WHEN sp.SalePrice = 0 THEN sp.Price*sp.QOH END DESC
Or alternatively just use the alias you defined in the result set:
ORDER BY
CASE WHEN sp.SalePrice > 0 THEN [sp.Value] END DESC,
CASE WHEN sp.SalePrice = 0 THEN [sp.StandardValue] END DESC
Note the brackets [].... this is required to define the column name as you used a dotted name in the alias... otherwise sp.Value would be considered to be the Value column in the sp table.

using ' around field names turns them into strings. Either remove the quotes entirely, or use " instead.

Related

Oracle CASE missing right parenthesis for a "in" limit

I have a QRY im developing in Oracle for spotfire. In the where statement, I have a decision case statement and if its True, im trying to pass a list of items to match a column, below is what I have, but its throwing a missing right parenthesis error and I cannot determine why. In short, when a variable is determined True (in this case 9>8 for the example, I need it to result those items, else, result the entire column with no limits.
Note: This works fine when its only 1 item being passed, i.e. 'BOB' but as soon as its multiple, this error occurs.
and Column1 = (CASE When 9>8 Then ('BOB','TOM') Else Column1 END)
Case expressions are best avoided in the where clause. Instead, write the logic with AND and OR:
And (
(9>8 AND Column1 IN ('BOB','TOM'))
OR 9<=8 -- You say you check a variable here, don't forget to check for NULL
)
Oracle does not have a boolean type for use in SQL queries.
Instead, just use basic logic:
and ( (9 > 8 and Column1 in ('BOB','TOM')) or
9 <= 8
)

Hive - SELECT inside WHEN clause of CASE function gives an error

I am trying to write a query in Hive with a Case statement in which the condition depends on one of the values in the current row (whether or not it is equal to its predecessor). I want to evaluate it on the fly, this way, therefore requiring a nested query, not by making it another column first and comparing 2 columns. (I was able to do the latter, but that's really second-best). Does anyone know how to make this work?
Thanks.
My query:
SELECT * ,
CASE
WHEN
(SELECT lag(field_with_duplicates,1) over (order by field_with_duplicates) FROM my_table b
WHERE b.id=a.id) = a.field_with_duplicates
THEN “Duplicate”
ELSE “”
END as Duplicate_Indicator
FROM my_table a
Error:
java.sql.SQLException: org.apache.spark.sql.AnalysisException: cannot recognize input near 'SELECT' 'lag' '(' in expression specification; line 4 pos 9
Notes:
The reason I needed the complicated 'lag' function is that the unique Id's in the table are not consecutive, but I don't think that's where it's at: I tested by substituting another simpler inner query and got the same error message.
Speaking of 'duplicates', I did search on this issue before posting, but the only SELECT's inside CASE's I found were in the THEN statement, and if that works the same, it suggests mine should work too.
You do not need the subquery inside CASE:
SELECT a.* ,
CASE
WHEN prev_field_with_duplicates = field_with_duplicates
THEN “Duplicate”
ELSE “”
END as Duplicate_Indicator
FROM (select a.*,
lag(field_with_duplicates,1) over (order by field_with_duplicates) as prev_field_with_duplicates
from my_table a
)a
or even you can use lag() inside CASE instead without subquery at all (I'm not sure if it will work in all Hive versions ):
CASE
WHEN lag(field_with_duplicates,1) over (order by field_with_duplicates) = field_with_duplicates
THEN “Duplicate”
ELSE “”
END as Duplicate_Indicator
Thanks to #MatBailie for the answer in his comment. Don't I feel silly...
Resolved

Error in ORDER BY clause using CASE WHEN

I'm trying to allow ASC/DESC sort order to be defined by a parameter in a stored procedure.
After lots of research, I found this approach (with simplification):
SELECT *
FROM MyTable
ORDER BY CASE WHEN #reverse = 1 THEN
MyColumn
END DESC,
CASE WHEN #reverse = 0 THEN
MyColumn
END ASC
However, this code throws the following error:
Msg 408, Level 16, State 1, Line 8
A constant expression was encountered in the ORDER BY list, position 2.
Why is this happening? Clearly MyColumn isn't a constant - it is a column name.
Using SQL Server 2016 in Compatibility Mode 2016 (130)
Thanks
After some search this line helped me understand more..
ordering by an expression must evaluate to a constant
so as Lamak pointed out,1=0 evaluates to false and you didn't define an else condition..so null is undefined and it throws error
to get rid of that try like below
ORDER BY CASE WHEN 1 = 1 THEN
MyColumn
END DESC,
CASE WHEN 1 = 0 THEN
col2 else col2--not your column,added this to make example clearer
END ASC
also beware ,expressions in order by must be unique,so your query won't work(even if it succeeds) and throws different error,you can use ISNULL as well
The real underlying cause of all this grief is the fact that:
ORDER BY MyColumn ASC
requires the
ASC
to be a hard-coded string (like SELECT, FROM, etc.) and can't be a string variable. ;-((
In order to overcome this limitation,
and the problem caused by trying to use CASE to overcome it,
I have made 95% of the query fill a table-variable,
then I have one of two queries which SELECT from it with the correct ORDER BY clause.

Assign a case value to a column rather than an alias

This should be a simple one, but I have not found any solution:
The normal way is using an alias like this:
CASE WHEN ac_code='T' THEN 'time' ELSE 'purchase' END as alias
When using alias in conjunction with UNION ALL this causes problem because the alias is not treated the same way as the other columns.
Using an alias to assign the value is not working. It is still treated as alias, though it has the column name.
CASE WHEN ac_code='T' THEN 'time' ELSE 'purchase' END as ac_subject
I want to assign a value to a column based on a condition.
CASE WHEN ac_code='T' THEN ac_subject ='time' ELSE ac_subject='purchase' END
Now I get the error message
UNION types character varying and boolean cannot be matched
How can I assign a value to a column in a case statement without using an alias in the column (shared by other columns in UNION)?
Here is the whole (simplified) query:
SELECT hr_id,
CASE WHEN hr_subject='' THEN code_name ELSE hr_subject END
FROM hr
LEFT JOIN code ON code_id=hr_code
WHERE hr_job='123'
UNION ALL
SELECT po_id,
CASE WHEN po_subject='' THEN code_name ELSE po_subject END
FROM po
LEFT JOIN code ON code_id=po_code
WHERE po_job='123'
UNION ALL
SELECT ac_id,
CASE WHEN ac_code='T' THEN ac_subject='time' ELSE ac_subject='purchase' END
FROM ac
WHERE ac_job='123'
There is no alias in your presented query. You are confusing terms. This would be a column alias:
CASE WHEN hr_subject='' THEN code_name ELSE hr_subject END AS ac_subject
In a UNION query, the number of columns, column names and data types in the returned set are determined by the first row. All appended rows have to match the row type. Column names in appended rows (including aliases) are just noise and ignored. Maybe useful for documentation, nothing else.
The = operator does not assign anything in a SELECT query. It's the equality operator that returns a boolean value. TRUE if both operands are equal, etc. This returns a boolean value: ac_subject='time' Hence your error message:
UNION types character varying and boolean cannot be matched
The only way to "assign" a value to a particular output column in this query is to include it at the right position in the SELECT list.
The information in the question is incomplete, but I suspect you are also confusing the empty string ('') with the NULL value. A distinction that you need to understand before doing anything else with relational databases. Maybe start here. In this case you would rather use COALESCE to provide a default for NULL values:
SELECT hr_id, COALESCE(hr_subject, code_name) AS ac_subject
FROM hr
LEFT JOIN code ON code_id=hr_code
WHERE hr_job = '123'
UNION ALL
SELECT po_id, COALESCE(po_subject, code_name)
FROM po
LEFT JOIN code ON code_id=po_code
WHERE po_job = '123'
UNION ALL
SELECT ac_id, CASE WHEN ac_code = 'T' THEN 'time'::varchar ELSE 'purchase' END
FROM ac
WHERE ac_job = '123'
Just an educated guess, assuming type varchar. You should have added table qualification to column names to clarify their origin. Or table definitions to clarify everything.
The CASE expression is supposed to return a value, e.g. 'time'.
Your value is another expression subject ='time' which is a boolean (true or false).
Is this on purpose? Does the other query you glue with UNION have a boolean in that place, too? Probably not, and this is what the DBMS complains about.
I found the problem.
CASE WHEN hr_subject=’’ THEN code_name ELSE hr_subject END
The columns code_name and hr_subject was different length. This caused the unpredictable result. I think that aliases can work now.
Thank you for your support.

How do I sort a VARCHAR column in PostgreSQL that contains words and numbers?

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