How to Order by enum in Oracle DB - sql

I want to order by a string column where that column is an enumeration. For example:
+----+--------+----------------------+
| ID | NAME | STATUS |
+----+--------+----------------------+
| 1 | Serdar | ACTIVE |
| 2 | John | DEACTIVE |
| 3 | Jerry | WAITING_FOR_APPROVAL |
| 4 | Jessie | REJECTED |
+----+--------+----------------------+
I want to order by STATUS. It should sort the results such that the first result must have STATUS = WAITING_FOR_APPROVAL, then ACTIVE, then DEACTIVE and then REJECTED.
Is there any way to do that in SQL? Is there something like Comparator in java?

You can enumerate the values in a CASE statement and order by that
SELECT id, name, status
FROM your_table
ORDER BY (CASE status
WHEN 'WAITING_FOR_APPROVAL' THEN 1
WHEN 'ACTIVE' THEN 2
WHEN 'DEACTIVE' THEN 3
WHEN 'REJECTED' THEN 4
ELSE 5
END)

Related

Creating Temporary Columns in PostgreSQL

I have a table with the following:
| Customer | Order Count|
-----------------------
| 1 | 1 |
| 2 | 2 |
| 3 | 1 |
and I want to create an additional column so I end up with the following:
| Customer | Order Count| Status |
--------------------------------
| 1 | 1 | new |
| 2 | 2 | old |
| 3 | 1 | new |
How can I structure my query in order to do so?
edit: the logic for the status labeling is that new customers only have one order, and old customers have > 1
Assuming that 1 means "new" and 2 means "old", you can use a case expression:
select t.*,
case order_count
when 1 then 'new'
when 2 then 'old'
else '??'
end as status
from mytable t
Or, if you want to create a computed column:
alter table mytable
add column status varchar(10)
generated always as (
case order_count
when 1 then 'new'
when 2 then 'old'
else '??'
end
)
stored
;

SQL Order By with case when

I am trying to understand the ORDER BY with CASE WHEN.
My aim is to understand it fundamentally for that I had different use cases created
My base table is as below
| Name |
|--------|
| BPM |
| BXR |
| Others |
| XZA |
| XYZ |
| PQR |
| ABC |
Query 1: Basic ORDER BY
SELECT *
FROM City
ORDER BY Name
Query 1 Result:Gave correct output as below(Name column sorted in ascending order)
| Name |
|--------|
| ABC |
| BPM |
| BXR |
| Others |
| PQR |
| XYZ |
| XZA |
Query 2: I want Others at last
SELECT *
FROM City
ORDER BY CASE
WHEN Name = 'Others' THEN 1
ELSE 0
END
Query 2 Result: I got partially correct result.I got Others at last but other names I expected it to be in ascending order.They actually appear the way they are in base table.
| Name |
|--------|
| BPM |
| BXR |
| XZA |
| XYZ |
| PQR |
| ABC |
| Others |
I am also not getting what does 0 and 1 actually mean in the ORDER BY statement.
Query 3: I want BXR and Others at last.
SELECT *
FROM City
ORDER BY CASE
WHEN Name = 'BXR' THEN 1
WHEN Name = 'Others' THEN 2
ELSE 0
END
Query 3 Result: I got partially correct result.I got 'Others' and 'BXR' at last but other Name are not in alphabetical order.Same as seen in Query 2.Here also I am not understanding the significance of 0, 1,2
| Name |
|--------|
| BPM |
| XZA |
| XYZ |
| PQR |
| ABC |
| BXR |
| Others |
Query 4: I want Others and PQR at top.
SELECT *
FROM City
ORDER BY CASE
WHEN Name = 'PQR' THEN 0
WHEN Name = 'Others' THEN 1
ELSE 2
END
QUery 4 Result: I get PQR and Others at top but the remaining names are not in aplhabetical order.
| Name |
|--------|
| PQR |
| Others |
| BPM |
| BXR |
| XZA |
| XYZ |
| ABC |
My assumption about 0, 1, 2 is that they are just numbers deciding the "order" in which a record should be.
(The record having 0 should be kept first and if all other records have 1 then should be sorted alphabetically)
(If there are '0', '1','2', in record with 0 should be first, record with 1 should be second all other record having 2 should be sorted alphabetically)
Correct me if I am wrong with this
SQLFiddle
You need to add name also in order by
DEMO
SELECT *
FROM City
ORDER BY CASE
WHEN Name = 'PQR' THEN 0
WHEN Name = 'Others' THEN 1
ELSE 2
END,name
OUTPUT:
**Name**
PQR
Others
ABC
BPM
BXR
XYZ
XZA
We can also ORDER BY using FIELD:
SELECT *
FROM City
ORDER BY FIELD(Name, 'Others', 'PQR') DESC, name;
Demo
The behavior of FIELD is such that it will return 1 for Others, 2 for PQR, and 0 for any other name. So, we use a descending order to ensure that PQR appears first, followed by Others, followed all other names.
You may keep the name column in the else case as :
SELECT *
FROM City
ORDER BY CASE
WHEN Name = 'PQR' THEN 0
WHEN Name = 'Others' THEN 1
ELSE Name
END
since always numbers has precedence over alphabets.
SQL Fiddle Demo

Provide status of an Activity based on date column in a view in SQL Server

In the view have to provide Status based on date.
The condition is:
If one of the activities doesn't have a date for the person, then Status is Active
If all the activities have a date for the person, then Status is Not Active
How do I get STATUS for a person?
For example:
|PersonId|Name|Activity | Date |
+--------+----+---------+-----------+
| 1 |John| a | 01/01/2015|
| 1 |John| b | 03/12/2016|
| 1 |John| c | 02/13/2017|
| 2 |Sam | d | 06/01/2014|
| 2 |Sam | e | 05/18/2016|
| 2 |Sam | f | NULL |
Output in the view:
|PersonId|Name|Status |
+--------+----+----------+
| 1 |John|Not Active|
| 2 |Sam | Active |
You can do this with aggregation:
select personid, name,
(case when count(*) = count(date) then 'Not Active' else 'Active' end) as status
from t
group by personid, name;
I think Gordon's answer is better, but since I started typing this out, I figured i'd finish it anyway.
something like
declare #maxDate date = datefromparts(9999, 12, 31)
select case when max(isnull(Date, #MaxDate)) = #maxDate) then 'Active' else 'inactive'
from ...

Merging multiple rows according to an order

Suppose there are the following rows
| Id | MachineName | WorkerName | MachineState |
|----------------------------------------------|
| 1 | Alpha | Young | RUNNING |
| 1 | Beta | | STOPPED |
| 1 | Gamma | Foo | READY |
| 1 | Zeta | Zatta | |
| 2 | Guu | Niim | RUNNING |
| 2 | Yuu | Jaam | STOPPED |
| 2 | Nuu | | READY |
| 2 | Faah | Siim | |
| 3 | Iem | | RUNNING |
| 3 | Nyt | Fish | READY |
| 3 | Qwe | Siim | |
We want to merge these rows according to following priority :
STOPPED > RUNNING > READY > (null or empty)
If a row has a value for greatest priority, then value from that row should be used (only if it is not null). If it is null, a value from any other row should be used. The rows should be grouped by id
The correct output for the above input is :
| Id | MachineName | WorkerName | MachineState |
|----------------------------------------------|
| 1 | Beta | Foo | STOPPED |
| 2 | Yuu | Jaam | STOPPED |
| 3 | Iem | Fish | RUNNING |
What would be a good sql query to accomplish this? I tried using joins, but it did not work out.
You can view this as a case of the group-wise maximum problem, provided you can obtain a suitable ordering over your MachineState column—e.g. by using a CASE expression:
SELECT a.Id,
COALESCE(a.MachineName, t.MachineName) MachineName,
COALESCE(a.WorkerName , t.WorkerName ) WorkerName,
a.MachineState
FROM myTable a JOIN (
SELECT Id,
MIN(MachineName) AS MachineName,
MIN(WorkerName ) AS WorkerName,
MAX(CASE MachineState
WHEN 'READY' THEN 1
WHEN 'RUNNING' THEN 2
WHEN 'STOPPED' THEN 3
END) AS MachineState
FROM myTable
GROUP BY Id
) t ON t.Id = a.Id AND t.MachineState = CASE a.MachineState
WHEN 'READY' THEN 1
WHEN 'RUNNING' THEN 2
WHEN 'STOPPED' THEN 3
END
See it on sqlfiddle:
| id | machinename | workername | machinestate |
|----|-------------|------------|--------------|
| 1 | Beta | Foo | STOPPED |
| 2 | Yuu | Jaam | STOPPED |
| 3 | Iem | Fish | RUNNING |
You could save yourself the pain of using CASE if MachineState was an ENUM type column (defined in the appropriate order). It so happens in this case that a simple lexicographic ordering over the string value will yield the same result, but that's a coincidence on which you really shouldn't rely as it's bound to slip under the radar when someone tries to maintain this code in the future.
This is a prioritization query. One method uses variables. Another uses union all . . . this works if the states are not repeated for a given id:
select t.*
from table t
where machinestate = 'STOPPED'
union all
select t.*
from table t
where machinestate = 'RUNNING' and
not exists (select 1 from table t2 where t2.id = t.id and t2.machinestate in ('STOPPED'))
union all
select t.*
from table t
where machinestate = 'READY' and
not exists (select 1 from table t2 where t2.id = t.id and t2.machinestate in ('STOPPED', 'RUNNING'));
change MachineState as enum:
`MachineState` enum('READY','RUNNING','STOPPED') DEFAULT NULL
and sql is simple:
select t.id,state.machinename,state.workername,t.mstate from state,(select id,max(MachineState) mstate from state group by Id) t where t.mstate=state.machinestate and t.id=state.id;

Find and update specific duplicates in MS SQL

given below table:
+----+---------+-----------+-------------+-------+
| ID | NAME | LAST NAME | PHONE | STATE |
+----+---------+-----------+-------------+-------+
| 1 | James | Vangohg | 04333989878 | NULL |
| 2 | Ashly | Baboon | 09898788909 | NULL |
| 3 | James | Vangohg | 04333989878 | NULL |
| 4 | Ashly | Baboon | 09898788909 | NULL |
| 5 | Michael | Foo | 02933889990 | NULL |
| 6 | James | Vangohg | 04333989878 | NULL |
+----+---------+-----------+-------------+-------+
I want to use MS SQL to find and update duplicate (based on name, last name and number) but only the earlier one(s). So desired result for above table is:
+----+---------+-----------+-------------+-------+
| ID | NAME | LAST NAME | PHONE | STATE |
+----+---------+-----------+-------------+-------+
| 1 | James | Vangohg | 04333989878 | DUPE |
| 2 | Ashly | Baboon | 09898788909 | DUPE |
| 3 | James | Vangohg | 04333989878 | DUPE |
| 4 | Ashly | Baboon | 09898788909 | NULL |
| 5 | Michael | Foo | 02933889990 | NULL |
| 6 | James | Vangohg | 04333989878 | NULL |
+----+---------+-----------+-------------+-------+
This query uses a CTE to apply a row number, where any number > 1 is a dupe of the row with the highest ID.
;WITH x AS
(
SELECT ID,NAME,[LAST NAME],PHONE,STATE,
ROW_NUMBER() OVER (PARTITION BY NAME,[LAST NAME],PHONE ORDER BY ID DESC)
FROM dbo.YourTable
)
UPDATE x SET STATE = CASE rn WHEN 1 THEN NULL ELSE 'DUPE' END;
Of course, I see no reason to actually update the table with this information; every time the table is touched, this data is stale and the query must be re-applied. Since you can derive this information at run-time, this should be part of a query, not constantly updated in the table. IMHO.
Try this statement.
LAST UPDATE:
update t1
set
t1.STATE = 'DUPE'
from
TableName t1
join
(
select name, last_name, phone, max(id) as id, count(id) as cnt
from
TableName
group by name, last_name, phone
having count(id) > 1
) t2 on ( t1.name = t2.name and t1.last_name = t2.last_name and t1.phone = t2.phone and t1.id < t2.id)
If my understanding of your requirements is correct, you want to update all of the STATE values to DUPE when there exists another row with a higher ID value that has the same NAME and LAST NAME. If so, use this:
update t set STATE = (case when sorted.RowNbr = 1 then null else 'DUPE' end)
from yourtable t
join (select
ID,
row_number() over
(partition by name, [last name], phone order by id desc) as RowNbr from yourtable)
sorted on sorted.ID = t.ID