Error in order clause of partition by when using multiple case statement - sql

I want to use multiple case statement inside order by clause in partition by statement.
I have many columns so ,I am only posting the required one.
I have table customers which has:
Select
name,
ROW_NUMBER() OVER(PARTITON BY lastname, rollno
ORDER BY
CASE
WHEN
NVL(gender, address) IS NULL
then
a.effdate desc
else
CASE
WHEN
NVL(a.postoffc, a.mon) <= file.effdate
then
file.effdate
else
a.postoffc
END
desc, NVL(l4.covcode, a.pass)
end
)
rn
from
customers a;
If,i remove these case statement then my join with other tables and query is working fine.So,there is no problem in join statement or any other logic.The problem I get is when i used multiple case statement.I think my syntax is mistake.Please tell me how can solve this error.I need this case statement logic as mandatory.

You need to close the second CASE block before you declare the other ordering criteria. Also, you have an unwanted DESC within the expression, that should be placed after it.
ORDER BY
CASE
WHEN NVL(gender, address) IS NULL THEN a.effdate
ELSE CASE
WHEN NVL(a.postoffc, a.mon) <= file.effdate THEN file.effdate
ELSE a.postoffc
END
END desc, --> here
NVL(l4.covcode, a.pass)
But overall, I don't think that you need to nest the case expressions. This should work equally well, and is easier to follow:
ORDER BY
CASE
WHEN NVL(gender, address) IS NULL THEN a.effdate
WHEN NVL(a.postoffc, a.mon) <= file.effdate THEN file.effdate
ELSE a.postoffc
END desc,
NVL(l4.covcode, a.pass)

Related

How to combine CASE statement with Inner Join for Alphanumeric OrderBY

In this query, I am trying to select all distinct (alphanumeric) machine names and order them correctly (1,2,5,10,15 instead of 1,10,15,2,5). The CASE statement is proven to work when the LocalName is not joined by INNER JOIN, so I suspect this is where the problem lies.
SELECT DISTINCT MCGroup, VisionMachinePerformance.MCSAP, ZAssetRegister.LocalName
FROM [VisionMachinePerformance] INNER JOIN ZAssetRegister ON VisionMachinePerformance.MCSAP=ZAssetRegister.SAP_Number
ORDER BY
CASE WHEN PATINDEX('%[0-9]%',LocalName) > 1 THEN
LEFT(LocalName,PATINDEX('%[0-9]%',LocalName)-1)
ELSE LocalName END ,
CASE WHEN PATINDEX('%[0-9]%',LocalName) > 1 THEN
CAST(SUBSTRING(LocalName,PATINDEX('%[0-9]%',LocalName),LEN(LocalName)) as float)
ELSE NULL END
The error that is reported is "SQL Error (145): ORDER BY items must appear in the select list if SELECT DISTINCT is specified".
I have tried changing all references in the CASE statement to ZAssetRegister.LocalName and VisionMachinePerformance.LocalName without success.
Removing all of the CASE statement and ordering by LocalName does work, but with the wrong order as mentioned above (1,10,15,2,5).
Could anybody suggest how to make this work?
TIA!
You can separate both parts using a subquery:
SELECT * FROM (
SELECT DISTINCT MCGroup, VisionMachinePerformance.MCSAP, ZAssetRegister.LocalName
FROM [VisionMachinePerformance]
INNER JOIN ZAssetRegister ON VisionMachinePerformance.MCSAP=ZAssetRegister.SAP_Number
) DISTINCT_DATA
ORDER BY
CASE WHEN PATINDEX('%[0-9]%',LocalName) > 1
THEN LEFT(LocalName,PATINDEX('%[0-9]%',LocalName)-1)
ELSE LocalName END,
CASE WHEN PATINDEX('%[0-9]%',LocalName) > 1
THEN CAST(SUBSTRING(LocalName,PATINDEX('%[0-9]%',LocalName),LEN(LocalName)) as float)
ELSE NULL END

Random sorting with ORDER BY with CASE clause

I am testing ORDER BY clause with CASE, and came across this problem.
My test select statement:
SELECT to_date as "One", field1 as "Two"
FROM(
SELECT to_date('yyyy-mm-dd', '2017-10-10'), '333' union all
SELECT to_date('yyyy-mm-dd', '2017-09-09'), '111' union all
SELECT to_date('yyyy-mm-dd', '2017-09-09'), '222' union all
SELECT to_date('yyyy-mm-dd', '2017-09-09'), '' union all
SELECT to_date('yyyy-mm-dd', '2017-09-09'), ''
)
ORDER BY One DESC,
CASE when Two = '' then 1
else 0 end DESC
And it's result may vary in a way, that sorting by second column is random:
How should I modify CASE clause to avoid it?
In Oracle, an empty string '' is the identical to NULL so your query is:
ORDER BY
One DESC,
CASE when Two = NULL then 1 else 0 end DESC
When comparing values, the are possible states are:
Equality Result
------------------------ ------
value = value TRUE
value = other_value FALSE
value = NULL NULL
NULL = NULL NULL
Your CASE expression will only evaluate to 1 when the equality evaluates to TRUE and this will never be the result when at least one side of the equality is NULL.
What you want is to use IS NULL rather than = '':
ORDER BY
One DESC,
CASE WHEN Two IS NULL THEN 1 ELSE 0 END DESC,
Two DESC;
Which you can simplify to:
ORDER BY
One DESC,
Two DESC NULLS FIRST;
The default for DESC ordering is NULLS FIRST so you could further simplify it to:
ORDER BY
One DESC,
Two DESC;
However, I would not take it this far as you are better explicitly stating that you are expecting NULL values to be ordered before non-NULL so future developers know that that is your intended ordering (rather than just an unintentional side-effect of your query).
Add the column two as third order condition
ORDER BY One DESC,
CASE when Two = '' then 1 else 0 end DESC,
Two DESC
The second order condition only puts empty entries first and not more.

How to nest a CTE (Common Table Expression)

I have the below query
With max_cm1 as (select * from tableA)
Select * ,
CASE WHEN TO_CHAR(CCP2.END_DATE,'MM/DD/YYYY') <> '09/09/9000' THEN 'CLOSED'
WHEN MAX_CM1.MAX_ROLE_CM IS NOT NULL AND HIST.PCMUID IS NOT NULL THEN 'ASSIGNED'
ELSE 'UNASSIGNED'
END STATUS
from max_cm1
Now I need to filter on the case statement. How can I do this?
You can use an alias eg: m.
With max_cm1 as (select * from tableA)
Select m.* ,
CASE WHEN TO_CHAR(CCP2.END_DATE,'MM/DD/YYYY') <> '09/09/9000' THEN 'CLOSED'
WHEN MAX_CM1.MAX_ROLE_CM IS NOT NULL AND HIST.PCMUID IS NOT NULL THEN 'ASSIGNED'
ELSE 'UNASSIGNED'
END STATUS
from max_cm1 m;
In your case, you don't need a CTE unless you are joining it with other table with some expressions in CTE. Directly you can fetch from table A with the same method if you are only interested in select '*'.
Your question is unclear. Also, the query as given is somewhat confusing, as it qualifies some columns with table names (CCP2 and HIST) that don't appear elsewhere in the query. Further, as written there seems to be no purpose to the CTE at all.
I'm assuming that what you want is to include the given CASE expression in the result set, but also use it within the WHERE clause to filter the results (e.g. WHERE CASE ... END = 'CLOSED'. The simple way to do this is to repeat the CASE expression; but of course duplicating logic is never a good choice. So the better way, which I think is the point of your question, is to include that derived column in a CTE so you can then refer to it by name in the WHERE clause.
It also looks like you are probably running into the issue of trying to select all columns (*) plus a derived column. The way around this is to qualify the * with the table name, or an alias as indicated in one of the other answers.
Putting this all together, I believe you want something like the following. I'm keeping the column expressions (e.g. HIST.PCMUID) as you wrote them although as written they make no sense. I'm guessing that tableA really represents some join of multiple tables.
WITH max_cm1 AS (
SELECT tableA.* ,
CASE WHEN TO_CHAR(CCP2.END_DATE,'MM/DD/YYYY') <> '09/09/9000' THEN 'CLOSED'
WHEN MAX_CM1.MAX_ROLE_CM IS NOT NULL AND HIST.PCMUID IS NOT NULL THEN
'ASSIGNED'
ELSE 'UNASSIGNED'
END STATUS
FROM tableA
)
SELECT *
FROM max_cm1
WHERE status = 'CLOSED'

Select Case is not working with Order by

I was using a simple sql query and getting an ordered list, but when I changed some of the values in the column I'm sorting by, those rows were no longer being sorted correctly.
select distinct u.Email,
case
when l.region_id is null then 'EU'
else l.region_id
end
as Location
from TB_User u
left join cat..location l on l.location=u.Location
where u.Username in (....)
order by l.region_id
I have about 5 rows that returned null for their region_id so they would be at the top of the result set. When I added the case and replaced their value, they still remain at the top. Is there anyway to make these rows sort according to their given value?
You can use CASE also in the ORDER BY. But in this case it seems that you instead want to order by the column which uses the CASE.
ORDER BY Location
If you instead want the null-regions at the bottom:
ORDER BY CASE WHEN l.region_id is null THEN 0 ELSE 1 END DESC,
Location ASC
If your rdbms doesn't support this (like SQL-Server does) you have to repeat it:
ORDER BY CASE WHEN l.region_id IS NULL THEN 'EU' ELSE l.region_id END ASC
You just order by the column value, which is null.
If you want to order by the case statement, just copy it in the order by clause:
order by
case
when l.region_id is null then 'EU'
else l.region_id end
If you are using SQL, try within the SELECT statement, use:
ISNULL(l.region_id, 'EU') AS Location
and then
ORDER BY 2
This will make your query:
SELECT DISTINCT u.Email, ISNULL(l.region_id, 'EU') AS Location
FROM TB_User u
LEFT JOIN cat..location l ON l.location=u.Location
WHERE u.Username in (....)
ORDER BY 2

Trouble with GROUP BY CASE

The following query gives the error "#1241 - Operand should contain 1 column(s)" because of the (Department_Code, Course_Code) line. When I replace that with just (Course_Code) it works. However, that's not what I want
SELECT * FROM Classes
GROUP BY CASE
WHEN (1) THEN
Department_Code
ELSE CASE WHEN (2) THEN
(Department_Code, Course_Code)
ELSE Class_ID
END
END
How can I group by Department_Code, Course_Code when condition (2) is satisfied?
A case expression can only return a single value, so you need two case expressions. Also, use a single case expression for each instead of nesting two inside each other:
SELECT * FROM Classes
GROUP BY
CASE
WHEN (1) THEN
Department_Code
WHEN (2) THEN
Department_Code
ELSE
Class_ID
END,
CASE
WHEN (2) THEN
Course_Code
ELSE
1
END
Had the same problem but found a simpler way which was to construct a helper field that can then be referenced in the GROUP BY.
Or you can do the same conditional trick placing it in the GROUP BY so long as you Concat the fields.
SELECT *,
If( 'whatever field to check' = 2 , CONCAT(Department_Code,Course_Code), Course_Code) AS 'group1'
FROM Classes
GROUP BY group1