I have a table of subjects which I need to sort - if the subject is a core subject then it needs to come first (sorted by SortOrder), if it is not a core subject then it needs to sorted alphabetically.
I've set up a fiddle with an example table: http://sqlfiddle.com/#!18/4b80eb/1
And the code I've tried is:
SELECT * FROM subject
ORDER BY CASE
WHEN IsCore=1 THEN SortOrder
ELSE [Description]
END
I get this error 'Conversion failed when converting the varchar value 'Science' to data type int.'.
I was hoping to get subjects in the following order:
Maths
Reading
Writing
Art
Computing
Science
Can anybody suggest how I should be doing the sort?
A CASE expression returns a scalar value, and uses data type precedence to determine the return type. Clearly SortOrder is an int, and so has a higher data type precedence that the varchar datatype that Description is. Therefore you need to use a few expressions. I assume you want rows that have IsCore = 1 first, which are then ordered by SortOrder, and then use the column Description.
ORDER BY CASE WHEN IsCore = 1 THEN 0 ELSE 1 END,
CASE WHEN IsCore = 1 THEN SortOrder END,
Description;
Assuming IsCore is always 0/1, you could express this using a single condition as:
ORDER BY ROW_NUMBER() OVER (ORDER BY IsCore DESC,
SortOrder * - IsCore,
Description
)
Or:
ORDER BY ORDER BY IsCore DESC,
SortOrder * - IsCore,
Description
Here is a SQLfiddle.
However, Larnu's solution is the more interpretable one.
Here is how I would do it:
SELECT [SubjectId], [SortOrder], [Description], [IsCore]
FROM subject
ORDER BY IsCore DESC, -- Everything with 1 in IsCore will come first
CASE WHEN IsCore = 1
THEN [SortOrder] -- use SortOrder for IsCore 1
ELSE ROW_NUMBER() OVER(ORDER BY [Description]) -- row number is alphabeticaly oredered
END
Note that a case expression must return compatible data type in all it's branches, meaning that it only returns a single data type, which is the highest precedence type from the set of types in all it's branches.
Like this:
SELECT * FROM subject
ORDER BY
IsCore DESC,
CASE IsCore WHEN 1 THEN [SortOrder] END,
[Description]
The CASE statement is needed only to sort the rows with IsCore = 1.
See the demo.
Results:
SubjectId SortOrder Description IsCore
3 1 Maths 1
1 2 Reading 1
2 3 Writing 1
5 5 Art 0
6 6 Computing 0
4 4 Science 0
Related
I have the following SQL script,
Select * From
(Select To_Char(Bmret.Pricedate, 'dd-mm-yyyy') As Pricedate, Bmret.Bmval, Bmret.id
, Cast(Exp(Sum(Ln(Cast(Bmret.Bmval As number))) Over (Partition By bmret.id)) As Number) As Twr
, RANK() OVER (PARTITION BY bmret.id ORDER BY bmret.pricedate asc) AS rank
From Tab_A Bmret
Where 1=1
) B
Where 1=1
And B.Rank=1
;
, which provides me with the desired result of a column, twr, that contains the product of the elements in column Bmval across pricedates, grouped by id.
However, I obtain the following error: 01428. 00000 - "argument '%s' is out of range".
I am aware that the error stems from the part Cast(Exp(Sum(Ln(Cast(Bmret.Bmval As number))) Over (Partition By bmret.id)) As Number) of the code and in particular that the "parameter passed into the function was not a valid value". Hence, my question is, is there any way to identify the id with values that are not valid?
I am not allowed to share the sample data. I am sorry.
Thank you in advance.
Best regards,
Please check the value of Cast(Bmret.Bmval As number). It must be greater than 0.
For further read:
https://www.techonthenet.com/oracle/functions/ln.php
Oracle / PLSQL: LN Function This Oracle tutorial explains how to use
the Oracle/PLSQL LN function with syntax and examples.
Description The Oracle/PLSQL LN function returns the natural logarithm
of a number.
Syntax The syntax for the LN function in Oracle/PLSQL is:
LN( number ) Parameters or Arguments number The numeric value used to
calculate the natural logarithm. It must be greater than 0.
You need to define what will be the Ln(Cast(Bmret.Bmval As number)) if Bmret.Bmval <=0. If you define it as 0( which might not be correct for the calculation) then your query would be:
Select * From
(Select To_Char(Bmret.Pricedate, 'dd-mm-yyyy') As Pricedate, Bmret.Bmval, Bmret.id
, Cast(Exp(Sum(case when Cast(Bmret.Bmval As number)>0 then Ln(Cast(Bmret.Bmval As number)) else 0 end) Over (Partition By bmret.id)) As Number) As Twr
, RANK() OVER (PARTITION BY bmret.id ORDER BY bmret.pricedate asc) AS rank
From Tab_A Bmret
Where 1=1
) B
Where 1=1
And B.Rank=1;
As #Kazi said, and as earlier answers had already mentioned, the issue is with using ln() with a negative number or zero. The documentation says:
LN returns the natural logarithm of n, where n is greater than 0.
so you can identify the IDs with out-of-range values with:
select id from tab_a where bmval <= 0
As you want the product of several numbers, you probably still want to include those values; but then having a zero amongst them should make the result zero, one negative number should make the result negative, two should make it positive, etc.
You can use the absolute value of your numbers for the calculation, and at the same time count how many negative values there are - then if that count of negatives is an odd number, multiply the whole result by -1.
Adapting the answer to your previous question, and changing the table and column names to match this question, that would be:
select to_char(a1.pricedate, 'dd-mm-yyyy') as pricedate, b1.bm, a1.bmval,
round(cast(exp(sum(ln(cast(abs(a1.bmval) as binary_double))) over (partition by b1.bmik)) as number))
*
case
when mod(count(case when a1.bmval < 0 then pricedate end) over (partition by b1.bmik), 2) = 0
then 1
else -1
end as product
from tab_a a1
inner join benchmarkdefs b1 on (a1.id = b1.bmik);
db<>fiddle with a group that has two negatives (which cancel out), one negative (which is applied), and one with a zero - where the product ends up as zero, as you'd hopefully expect.
The point of the cast() calls was to improve performance, as noted in the old question I linked to, by performing the exp/ln part as binary_double; there is no point casting a number to number. If you don't want the binary_double part then you can take the casts out completely; but then you do also have to deal with zeros as well as negative values, e.g. keeping track of whether you have any of those too:
select to_char(a1.pricedate, 'dd-mm-yyyy') as pricedate, b1.bm, a1.bmval,
round(exp(sum(ln(abs(nullif(a1.bmval, 0)))) over (partition by b1.bmik)))
*
case when min(abs(a1.bmval)) over (partition by b1.bmik) = 0 then 0 else 1 end
*
case
when mod(count(case when a1.bmval < 0 then pricedate end) over (partition by b1.bmik), 2) = 0
then 1
else -1
end as product
from tab_a a1
inner join benchmarkdefs b1 on (a1.id = b1.bmik);
db<>fiddle
For this query, which just gets values for the first date and product across all dates, that would translate (with casting) to:
select * from
(
select to_char(bmret.pricedate, 'dd-mm-yyyy') as pricedate, bmret.bmval, bmret.id
, round(exp(sum(ln(abs(nullif(bmret.bmval, 0)))) over (partition by bmret.id)))
*
case when min(abs(bmret.bmval)) over (partition by bmret.id) = 0 then 0 else 1 end
*
case
when mod(count(case when bmret.bmval < 0 then pricedate end) over (partition by bmret.id), 2) = 0
then 1
else -1
end as twr
, rank() over (partition by bmret.id order by bmret.pricedate asc) as rank
from tab_a bmret
) b
where b.rank=1
PRICEDATE
BMVAL
ID
TWR
RANK
11-08-2021
1
1
120
1
11-08-2021
12
2
524160
1
11-08-2021
22
3
-7893600
1
11-08-2021
1
4
0
1
db<>fiddle
As you were told in an old answer, if you don't want to see the (not very interesting) rank column then change select * from to select pricedate, bmval, id, twr from in the outer query.
You could also use aggregation with keep to avoid needing an inline view:
select to_char(min(pricedate), 'dd-mm-yyyy') as pricedate
, min(bmret.bmval) keep (dense_rank first order by pricedate) as bmval
, min(bmret.id) keep (dense_rank first order by pricedate) as id
, round(exp(sum(ln(abs(nullif(bmret.bmval, 0))))))
*
case when min(abs(bmret.bmval)) = 0 then 0 else 1 end
*
case
when mod(count(case when bmret.bmval < 0 then pricedate end), 2) = 0
then 1
else -1
end as twr
from tab_a bmret
group by bmret.id
PRICEDATE
BMVAL
ID
TWR
11-08-2021
1
1
120
11-08-2021
12
2
524160
11-08-2021
22
3
-7893600
11-08-2021
1
4
0
db<>fiddle
This is the original table:
I have 2 different query and I want to make 1 query for these:
SELECT *
FROM SAMPLE
WHERE ORDER_PRIORITY<40
ORDER BY FS_GENERATE_DATE IS NOT NULL, FS_GENERATE_DATE,ORDER_PRIORITY,CREATE_ID,CR_DATE,ORDER_QTY;
second:
SELECT *
FROM SAMPLE
WHERE ORDER_PRIORITY>=40
ORDER BY FS_GENERATE_DATE IS NOT NULL, FS_GENERATE_DATE,ORDER_PRIORITY,CREATE_ID,CR_DATE,ORDER_QTY;
I need the next result in only 1 query:
if the order_priority<40 than the order will be the first according to the order by
but if order_priority>=40 than these data will be after the lower priority (first conditional /op<40/).
Result:
You can add this to your order by clause:
case when ORDER_PRIORITY<40 then 0 else 1 end
The final query will be:
SELECT
*
FROM SAMPLE
WHERE ORDER_PRIORITY>=40
ORDER BY
case when ORDER_PRIORITY<40 then 0 else 1 end,
FS_GENERATE_DATE IS NOT NULL, FS_GENERATE_DATE,ORDER_PRIORITY,CREATE_ID,CR_DATE,ORDER_QTY;
You are clearly using a database where booleans are allowed in the ORDER BY. So, you can just use:
SELECT S.*
FROM SAMPLE S
ORDER BY (ORDER_PRIORITY < 40) DESC,
FS_GENERATE_DATE IS NOT NULL,
FS_GENERATE_DATE,
ORDER_PRIORITY,
CREATE_ID, CR_DATE, ORDER_QTY;
I have a table here :
I want to reward a gold and a silver(i.e a value of 1 wherever applicable,else 0 )to top 2 persons by looking at pointsRewarded field.
I already have the first table created .I have modified it with the extra fields that I want i.e rank,gold,silver fields.It has null values now.
i want the output to be something like this:
I have tried some query :
update dbo.original
set rnk = dense_rank() over (partition by WeekNumber order by pointsrewarded desc)
set gold =
case when rnk = '1' then 1 else 0 end
set silver =
case when rnk = '2' then 1 else 0 end
I already have modified the table design by adding the rnk,gold and silver fields.I want the values generated by the query to go and sit in those fields.
Please help me with the query or give me some suggestions on how to proceed.Please.
Thanks a lot.
This seems like a follow-up to your earlier question.
In SQL Server you can use an updatable CTE:
with toupdate as (
select o.*,
dense_rank() over (partition by WeekNumber order by pointsrewarded desc) as new_rnk
from dbo.original
)
update toupdate
set rnk = new_rank,
gold = (case when rnk = 1 then 1 else 0 end),
silver = (case when rnk = 2 then 1 else 0 end);
Note: Only use single quotes for string and date constants. Do not use single quotes for numeric constants.
I have used the case in order by when complaint status id = 1 then sort data by complaint date and complaint status id = 2 then sort the data by Resolved on date and when complaint status id = 3 then sort data by resolved on date
But when the complaint status id = 3 then it give the error message:
conversion failed when converting date and / or time form character
string
Order by in query
order by case #ComplaintstatusId when 1 then a.complaintDate
when 2 then a.resolvedDate
when 3 then a.resolvedDate
else 1 end desc
You are projecting 1 of 3 dates, or an integer from your case when statement - this won't fly - all returns from the case when must be of the same type. I'm not sure what you are trying to do with 1, but for example:
SELECT * FROM SomeTable a
order by
case #ComplaintstatusId
when 1 then a.complaintDate
when 2 then a.resolvedDate
when 3 then a.resolvedDate
else CURRENT_TIMESTAMP -- same type as the others
end desc
Edit
I'm guessing that by 1 desc you mean ordering desc by the first column (which is of a different type, such as *char, hence the error). What you can do then is use two column orderings, mutually exclusive from eachother:
SELECT * FROM SomeTable a
order by
case #ComplaintstatusId
when 1 then a.complaintDate
when 2 then a.resolvedDate
when 3 then a.resolvedDate
end desc,
case when #ComplaintstatusId < 1 or #ComplaintstatusId > 3
then MyCol -- First column in your select list
end desc;
AFAIK you'll need to explicitly name column 1.
SqlFiddle here
I need help with writing a select clause query.
For example, lets say I have a query like that:
select value from some_table order by value asc;
as a result I get this:
1
2
3
4
5
6
7
8
9
10
but a special query I want to write, is the one which still will give me sorted values, but will put 5 after 8.
this means I need one value to be out of regular order.
it can be described in other way. lets say I have two groups of numbers (example):
A={ a | 1<=a<=118, a!=78 } B={ b | b>118 }
I have a group C=A U B U {78}
and I need all these values sorted like "A,78,B"
Assuming value is integer, you could do this:
SELECT *
FROM tbl
ORDER BY
CASE
WHEN value = 5 THEN 8.5
ELSE value
END
Or to expand upon DCP's answer...
SELECT *
FROM tbl
ORDER BY
CASE
WHEN (Condition for first grouping) THEN 1
WHEN (Condition for second grouping) THEN 2
WHEN (Condition for third grouping) THEN 3
ELSE 4
END
You can use multiple conditions in your order by:
ORDER BY (value BETWEEN 1 AND 118) AND value != 78 DESC,
value > 118 DESC,
value
This will ensure that values which match the first predicate come first, then values matching the second predicate, and finally values matching none of the predicates. If there is a tie (two numbers matching the same predicate) then these numbers are sorted in ascending order.
Note that I haven't tested this in Oracle. It might be necessary to wrap the predicate in a CASE expression (CASE WHEN predicate THEN 1 ELSE 0 END) to get the sorting to work in Oracle.
ORDER BY
(CASE WHEN ((value BETWEEN 1 AND 118) AND value <> 78) THEN 1 ELSE 0 END) DESC,
(CASE WHEN (value > 118) THEN 1 ELSE 0 END) DESC,
value
Order by some CASE-expression to remap your values.