Select Case is not working with Order by - sql

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

Related

How to use case statement and min() with group by?

The following query when I that execute
SELECT CASE
WHEN spd.IS_MAIN_DEFECT='Y'
THEN spd.piece_Defect_num_id
ELSE min(spd.PIECE_DEFECT_NUM_ID)
END AS defect
FROM piece P,
STY_PIECE_DEFECT spd,
STY_DEFECT_CATALOGUE sdc,
piece_history ph,
piece_history_out pho,
PLANT_CONFIG pc
(...inner join and where clause)
GROUP BY p.PIECE_ID,
CASE
WHEN spd.IS_MAIN_DEFECT='Y'
THEN spd.piece_Defect_num_id
ELSE min(spd.PIECE_DEFECT_NUM_ID)
end
It seems error
ORA-00934: group function is not allowed here
I guess , there is error min() in group by.
How can I solve this problem?
You have to use analytical MIN() function like the below without group by
SELECT distinct CASE WHEN spd.IS_MAIN_DEFECT='Y'
THEN spd.piece_Defect_num_id
ELSE min(spd.PIECE_DEFECT_NUM_ID) over () END AS defect
FROM piece P , STY_PIECE_DEFECT spd ,STY_DEFECT_CATALOGUE sdc ,piece_history ph
, piece_history_out pho, PLANT_CONFIG pc
(...inner join and where clause)
I understood you want to preserve the PIECE_DEFECT_NUM_ID in rows with IS_MAIN_DEFECT = 'Y' and get the MIN value for the non-main defect lines.
The simplest solution for the case there is only one row per PIECE_ID with IS_MAIN_DEFECT = 'Y' - you group on PIECE_ID, IS_MAIN_DEFECTand calculates min(PIECE_DEFECT_NUM_ID)
which is fine a MIN for one row group is equal to the original value
select PIECE_ID,IS_MAIN_DEFECT,
min(PIECE_DEFECT_NUM_ID) PIECE_DEFECT_NUM_ID
from tab
group by PIECE_ID, IS_MAIN_DEFECT
order by 1,2 desc;
In case you can have more rows (same PIECE_ID) with the main flag, simple select them ungrouped and add the grouped non-main part using UNION ALL
select PIECE_ID,IS_MAIN_DEFECT,PIECE_DEFECT_NUM_ID
from tab
where IS_MAIN_DEFECT = 'Y'
UNION ALL
select PIECE_ID,IS_MAIN_DEFECT,
min(PIECE_DEFECT_NUM_ID) PIECE_DEFECT_NUM_ID
from tab
where nvl(IS_MAIN_DEFECT,'N') != 'Y'
group by PIECE_ID, IS_MAIN_DEFECT

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

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)

Why does this not return 0

I have a query like:
select nvl(nvl(sum(a.quantity),0)-nvl(cc.quantityCor,0),0)
from RCV_TRANSACTIONS a
LEFT JOIN (select c.shipment_line_id,c.oe_order_line_id,nvl(sum(c.quantity),0) quantityCor
from RCV_TRANSACTIONS c
where c.TRANSACTION_TYPE='CORRECT'
group by c.shipment_line_id,c.oe_order_line_id) cc on (a.shipment_line_id=cc.shipment_line_id and a.shipment_line_id=7085740)
where a.transaction_type='DELIVER'
and a.shipment_line_id=7085740
group by nvl(cc.quantityCor,0);
The query runs OK, but returns no value. I want it to return 0 if there is no quantity found. Where have I gone wrong?
An aggregation query with a GROUP BY returns no rows if all rows are filtered out.
An aggregation query with no GROUP BY always returns one row, even if all rows are filtered out.
So, just remove the GROUP BY. And change the SELECT to:
select coalesce(sum(a.quantity), 0) - coalesce(max(cc.quantityCor), 0)
I may be wrong, but it seems you merely want to subtract CORRECT quantity from DELIVER quantity for shipment 7085740. You don't need a complicated query for that. Especially your GROUP BY clauses make no sense if that is what you are after.
One way to write this query would be:
select
sum(case when transaction_type = 'DELIVER' then quantity else 0 end) -
sum(case when transaction_type = 'CORRECT' then quantity else 0 end) as diff
from rcv_transactions
where shipment_line_id = 7085740;
I had a query like this and was trying to return 'X' when the item is not valid.
SELECT case when segment1 is not null then segment1 else 'X' end
--INTO v_orgValidItem
FROM mtl_system_items_b
WHERE segment1='1676001000'--'Jul-00'--l_item
and organization_id=168;
..but it was returning NULL.
Changed to use aggregation with no group by and now it returns 'X' when the item is not valid.
SELECT case when max(segment1) is not null then max(segment1) else 'X' end valid
--INTO v_orgValidItem
FROM mtl_system_items_b
WHERE segment1='1676001000'--'Jul-00'--l_item
and organization_id=168;--l_ship_to_organization_id_pb;
Here is another example, proving the order of operations really matters.
When there is no match for this quote number, this query returns NULL:
SELECT MAX(NVL(QUOTE_VENDOR_QUOTE_NUMBER,0))
FROM PO_HEADERS_ALL
WHERE QUOTE_VENDOR_QUOTE_NUMBER='foo.bar';
..reversing the order of MAX and NVL makes all the difference. This query returns the NULL value condition:
SELECT NVL(MAX(QUOTE_VENDOR_QUOTE_NUMBER),0)
FROM PO_HEADERS_ALL
WHERE QUOTE_VENDOR_QUOTE_NUMBER='foo.bar';

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.

Adjust SQL Query to force a record to appear first?

How can the below query be adjusted to return always the member with MemberID = 'xxx' as the first row
SELECT * FROM Members
select * from Members
order by case when MemberID = XXX then 0 else 1 end
This should work and it will also allow you to order the remaining items by MemberID (Assuming xxx=12 in this example)
SELECT *
FROM Members
ORDER BY CASE WHEN MemberID=12 THEN NULL ELSE isnull(MemberID,0) END
If the memberID column can't contain nulls, you can get away with this which might perform slightly better.
SELECT *
FROM Members
ORDER BY CASE WHEN MemberID=12 THEN NULL ELSE MemberID END
SELECT
CASE WHEN MemberID = 'xxx' AS 1 ELSE 0 END CASE AS magic,
*
FROM Members
ORDER BY magic DESC
The syntax might vary depending on yr db, but I hope you get the idea.
SELECT * FROM `Members` WHERE `MemberID` = '[ID]' LIMIT 1 UNION SELECT * FROM `Members`
This should work. Tested on my database instance. Chosen ID is always first.
A more robust solution, if you have more than one record that has to be floated to the top, or if you have a specific order for multiple records, is to add a ResultsOrder column to your table, or even another table MemberOrder(memberid, resultorder). Fill resultorder with big numbers and ...
Select m.*
From Members m
Left Join MemberOrder mo on m.MemberID=mo.MemberID
Order by coalesce(mo.resultorder, 0) DESC
try this:
SELECT * FROM Members
ORDER BY IF(x.MemberId = XXX, -1, ABS(x.MemberId))