Unable to ORDER BY on Column Alias when using CASE - sql

I have a column that will show 'active' if the 'Expiration Date' in the empTable is < Current Date, and will show 'inactive' otherwise, then I want to sort that column so that all 'active' employees will be at the top.
CREATE PROCEDURE SOME_SP
#SortBy VARCHAR(10)
AS
SELECT emp.empname,
CASE WHEN (emp.expiration_date < CURRENT_TIMESTAMP) THEN 'Active' ELSE 'InActive' END AS Emp_Status, DeptName
FROM empTable emp, Dept dpt
WHERE emp.ID = dpt.ID
CASE #sortBy WHEN 'NAME' THEN emp.empName END,
CASE #sortBy WHEN 'STATUS' THEN Emp_Status END
If the user enter 'NAME', then the SP will sort by emp.empName, which works fine, but not the STATUS
I get an error saying that Invalid column name 'Emp_Status'
What did I do wrong?
Edited: I'm so sorry, I realize this query works if it is in plain SQL. However, the fact is I'm doing it in the Stored Procedure where the user can specify which column to sort. I will post a more complete SP above.

Okay, here are some comments that I have.
1 - Please get away from old style joins. Use the INNER JOIN ON clause.
2 - There is no reason why an alias can not be used in the ORDER BY clause. Please see Itzik Ben-Gans posting on logical processing order. The SELECT arguments are processed way before the ORDER BY.
http://www.sql.co.il/books/insidetsql2008/Logical%20Query%20Processing%20Poster.pdf
3 - Last but not least, a simple example (adventureworks) that make everyone with a hire date less than 2004 as Active, everyone else is in-active. This will sort by the status column.
Good luck.
John
-- Sample database
Use AdventureWorks2012
GO
-- Sample select showing alias works fine in a order by clause.
SELECT [LoginID] as login_id,
CASE WHEN (e.HireDate < '20040101') THEN 'Active'
ELSE 'InActive' END AS emp_status
FROM [HumanResources].[Employee] as e
ORDER BY emp_status desc
GO
Since you changed your code above, here is a new answer to match. For a CASE statement on an ORDER BY you have to use the actual columns. For just a simple ORDER BY, the alias will work.
SO THE ANSWER is it ALL DEPENDS!!
Use AdventureWorks2012
GO
ALTER PROCEDURE usp_Sort_By_Column(#sort varchar(25))
AS
SELECT
[LoginID] as login_id,
CASE WHEN (e.HireDate < '20040101')
THEN 'Active' ELSE 'InActive' END AS emp_status
FROM
[HumanResources].[Employee] as e
ORDER BY
(CASE
WHEN #sort = 'ID' THEN [LoginID] ELSE
(CASE WHEN (e.HireDate < '20040101')
THEN 'Active' ELSE 'InActive' END)
END)
GO
usp_Sort_By_Column 'STATUS'
Link to ORDER BY - Books On Line ...
http://msdn.microsoft.com/en-us/library/ms188385.aspx
Best answer for your crazy query: Make a sort column that is dynamic using the variable. Just order by the first column. Cleanest answer.
Use AdventureWorks2012
GO
ALTER PROCEDURE usp_Sort_By_Column(#sort varchar(25))
AS
SELECT
(CASE
WHEN #sort = 'ID' THEN [LoginID] ELSE
(CASE WHEN (e.HireDate < '20040101')
THEN 'Active' ELSE 'InActive' END)
END) as Sort_Column,
[LoginID] as login_id,
CASE WHEN (e.HireDate < '20040101')
THEN 'Active' ELSE 'InActive' END AS emp_status
FROM
[HumanResources].[Employee] as e
ORDER BY
1
GO
usp_Sort_By_Column 'ID'

Related

Join 2 Queries and a Pivot in SQL

I have 2 queries that I want to join together. They come from the same table, but the organization and structure of columns is bad. Hence the need for a Pivot in Query 1, then join it to Query 2.
I do know that this needs to be done with a CTE, but my output either results in a blank query, or info in the wrong columns.
I'm using SSMS18. Thanks for the help.
Query 1
Select *, '' AS 'DNC', '' AS 'DNF', '' AS 'DNR' from
(Select
Opin,
Qty,
[TicketDate],
[TicketNumber],
[Cust],
[ProdID]
from [dbo].[obser]) AS ID
Pivot (
SUM([Qty])
For [Opin] IN ([ArvUnits],[DeliveryUnits])
) AS SingleOTRow
where
TicketDate between '2021-01-01' and '2021-03-15'
and ProdID like '%_LB%'
Query 2
select
IOA.Cust, IOA.TicketNumber, IOA.TicketDate, '' AS 'ProdID','' AS 'IOA QTY', '' AS 'Delivery Units', IOA.ProdID AS 'DNC',
case
When IOA.Qty >=1 Then '1'
else 'N'
end AS 'DNF',
CASE
when IOA.ProdID = '21' Then 'Full'
else 'Not a Valid DNC- Review'
END as 'DNR'
from [dbo].[obser] AS IOA
where
IOA.TicketDate between '2021-01-01' and '2021-03-15' and
IOA.Opin= 'DNS'
Sample Data
[Sample Data]

SQL - Subselect in select clause - how to create column which decides uniqity logic

I am trying to write subselect which will run through returned data, then checks status of all and then decides uniquity logic.
Is there any way to find out following ?
case any of data has 'Active' status first one will be marked as 1 everything else as 0
case there is no 'Active' status then first 'Expired' status will by marked as 1 and everything else as 0
case there is no 'Active' and 'Expired' status then first 'In Progress' will be marked as 1 and everything else as 0
I was trying to write it like this but i need to have it in one case statement
SELECT a.id, a.status,
,(SELECT
CASE WHEN b.STATUS = 'Active' THEN 1 ELSE 0 END
CASE WHEN b.STATUS = 'Expired' THEN 1 ELSE 0 END
FROM b.TABLE
WHERE a.id=b.id )AS unique
FROM my.TABLE
Result should look like https://i.stack.imgur.com/qCA74.png picture for expired case
Thank you in advance for any tips.
Use a window function:
select t.*,
(case when row_number() over (partition by id
order by case status when 'Active' then 1 when 'Expired' then 2 else 3 end
) = 1
then 1 else 0
end) as unique_flag
from my.table t;
If the lookup table is the same as source table, then you can use LAG function with constant and use its default value to mark the first row with 1 and others with 0. But you need to order your rows by some fields to deal with duplicates on status.
select a.id, a.status,
lag(0, 1, 1) over(
partition by a.id
order by
case a.status
when 'Active' then 0
when 'Expired' then 1
else 3
end asc,
a.some_more_columns asc /*To find that first row when there are duplicates by status*/
) as unique_flag
from MY_TABLE a
And what about object naming: never use keywords as identifiers. Calling column with date as date, table with users as users and some unknown table as table makes you design error prone.

Joining a Temp Table to Actual Table

I need to verify that each order has been acknowledged. The problem is that each order can have multiple codes. The query I had (utilizing a CASE statement) would check for blank fields or fields with the string "None" to verify the order has not been acknowledged. It would return the appropriate result, but multiple rows (once for each possible response) and I only need (1).
I'm attempting to create a temp table that will return the appropriate result and join (via an order unique ID) the two tables together hoping to correct the multiple row issue. Here is the code:
DROP TABLE staging_TABLE;
CREATE TEMP TABLE staging_TABLE(
ORDERID varchar(256) ,
CODE varchar(256) );
/*Keeping data types consistent with the real table*/
INSERT INTO staging_TABLE
SELECT ORDERID,
CASE CODE
WHEN 'None' THEN 'No'
WHEN '' THEN 'No'
ELSE 'Yes'
END
FROM ORDERS
WHERE UTCDATE > SYSDATE - 10
AND CODE IS NOT NULL;
SELECT R.QUESTION,
R.ORDERNAME,
T.CODE
FROM ORDERS R
INNER JOIN staging_TABLE T
ON R.ORDERID= T.ORDERID
WHERE R.UTCDATE > SYSDATE - 10
AND R.CODE IS NOT NULL
AND R.CATEGORY IS NOT NULL
AND R.UTCDATE IS NOT NULL
GROUP BY
R.ORDER,
T.CODE,
R.ORDERNAME,
R.CODE
ORDER BY
R.ORDERNAME,
R.ORDER;
Am I doing this correctly? Or is this even the right approach?
Am I doing this correctly? Or is this even the right approach?
No. You don't need a temp table for this. Your query might look like this:
SELECT question, ordername
, CASE WHEN code IN ('None', '') THEN 'No' ELSE 'Yes' END AS code
FROM orders
WHERE utcdate > sysdate - 10
AND code IS NOT NULL
AND category IS NOT NULL
GROUP BY question, ordername, 3, "order"
ORDER BY ordername, "order";
ORDER is a reserved word. It's not possible to use it as column name unless double quoted. There is something wrong there.
AND R.UTCDATE IS NOT NULL is redundant. It can't be NULL anyway with WHERE R.UTCDATE > SYSDATE - 10
3 in my GROUP BY clause is a positional reference to the CASE expression. Alternatively you can spell it out again:
....
GROUP BY question, ordername
, CASE WHEN code IN ('None', '') THEN 'No' ELSE 'Yes' END
, "order"
You can use the DISTINCT keyword as follows so you will not need a temp table:
SELECT DISTINCT QUESTION,
ORDERNAME,
CASE CODE
WHEN 'None' THEN 'No'
WHEN '' THEN 'No'
ELSE 'Yes'
FROM ORDERS
WHERE UTCDATE > SYSDATE - 10
AND CODE IS NOT NULL
AND CATEGORY IS NOT NULL
AND UTCDATE IS NOT NULL
ORDER BY 2,3;

T SQL Chain join 4 tables

Here is the scenario:
I have tables:
Articles (articleId, userId, title, datePosted)
ArticleTranslations (languageId, articleId, title) (not so important for this case, but I am showing anyway)
ArticleSections (articleSectionId, articleId, sectionId)
sections (sectionId, content, ...)
sectionsAdditionalInfo (sectionId, isApproved)
What I am doing is selecting some articles from articles by userId in a way:
SELECT article.articleId, article.userId, ArticleTranslations.title, article.datePosted
FROM Articles
LEFT OUTER JOIN ArticleTranslations ON Article.articleId= ArticlesTranslations.articleId AND ArticlesTranslations.languageId=#languageId
WHERE Articloes.userId=#userId
ORDER BY
CASE WHEN #sortDirection = 'DD' THEN datePosted END DESC, -- by date posted
CASE WHEN #sortDirection = 'DA' THEN datePosted END,
CASE WHEN #sortDirection = 'ND' THEN title END DESC, -- sort by name
CASE WHEN #sortDirection = 'NA' THEN title END,
CASE WHEN #sortDirection = 'SD' THEN ArticleTranslations.isApproved END DESC, -- is article aproved?
CASE WHEN #sortDirection = 'SA' THEN ArticleTranslations.isApproved END,
CASE WHEN #sortDirection = 'ID' THEN areAllSectionsApproved END DESC, -- sort by information if sections are all approved - within the article?
CASE WHEN #sortDirection = 'IA' THEN areAllSectionsApproved END
Please bare in mind I left out some of the info in order for my question to be more understandable.
Now, what I would like to do is select another attribute (for each article returned in SQL above): areAllArticleSectionsApproved
I have assembled SQL separately, but I would like this to be returned for every row:
SELECT CASE WHEN COUNT(sectionsAdditionalInfo.sectionId) > 0 THEN 0 ELSE 1 END AS areAllSectionsApproved
FROM ArticleSections
LEFT OUTER JOIN sectionsAdditionalInfo ON ArticleSections.sectionId = sectionsAdditionalInfo.sectionId
WHERE articleId=#articleId AND sectionsAdditionalInfo.isApproved=0
I have tried nesting this SQL, in a way:
SELECT (outer SQL) .....
a.*(
nested SQL - second one I posted here
) as a
but it didn't work at all.
I am using SQL server 2008.
Any hint on how to solve this would be greatly appreciated ;)
Without fully understanding your data and your desired results, something like this should work using your above query and joining on your second query as a subquery:
SELECT article.articleId,
article.userId,
ArticleTranslations.title,
article.datePosted,
t.areAllSectionsApproved
FROM Articles
LEFT OUTER JOIN ArticleTranslations
ON Article.articleId= ArticlesTranslations.articleId
AND ArticlesTranslations.languageId=#languageId
LEFT JOIN (
SELECT
articleId,
CASE
WHEN COUNT(sectionsAdditionalInfo.sectionId) > 0
THEN 0
ELSE 1
END AS areAllSectionsApproved
FROM ArticleSections
LEFT OUTER JOIN sectionsAdditionalInfo
ON ArticleSections.sectionId = sectionsAdditionalInfo.sectionId
WHERE sectionsAdditionalInfo.isApproved=0
GROUP BY articleId
) t ON articles.articleId = t.articleId
WHERE Articloes.userId=#userId
ORDER BY
CASE WHEN #sortDirection = 'DD' THEN datePosted END DESC, -- by date posted
CASE WHEN #sortDirection = 'DA' THEN datePosted END,
CASE WHEN #sortDirection = 'ND' THEN title END DESC, -- sort by name
CASE WHEN #sortDirection = 'NA' THEN title END,
CASE WHEN #sortDirection = 'SD' THEN ArticleTranslations.isApproved END DESC, -- is article aproved?
CASE WHEN #sortDirection = 'SA' THEN ArticleTranslations.isApproved END,
CASE WHEN #sortDirection = 'ID' THEN areAllSectionsApproved END DESC, -- sort by information if sections are all approved - within the article?
CASE WHEN #sortDirection = 'IA' THEN areAllSectionsApproved END
Good luck.

CASE .. WHEN expression in Oracle SQL

I have the table with 1 column and has following data
Status
a1
i
t
a2
a3
I want to display the following result in my select query
Status| STATUSTEXT
a1 | Active
i | Inactive
t | Terminated
a2 | Active
a3 | Active
One way I could think was using a Switch When expression in select query
SELECT
status,
CASE status
WHEN 'a1' THEN 'Active'
WHEN 'a2' THEN 'Active'
WHEN 'a3' THEN 'Active'
WHEN 'i' THEN 'Inactive'
WHEN 't' THEN 'Terminated'
END AS StatusText
FROM stage.tst
Is there any other way of doing this where I don't need to write When expression 3 times for Active Status and the entire active status can be checked in one single expression?
You could use an IN clause
Something like
SELECT
status,
CASE
WHEN STATUS IN('a1','a2','a3')
THEN 'Active'
WHEN STATUS = 'i'
THEN 'Inactive'
WHEN STATUS = 't'
THEN 'Terminated'
END AS STATUSTEXT
FROM
STATUS
Have a look at this demo
SQL Fiddle DEMO
You can rewrite it to use the ELSE condition of a CASE:
SELECT status,
CASE status
WHEN 'i' THEN 'Inactive'
WHEN 't' THEN 'Terminated'
ELSE 'Active'
END AS StatusText
FROM stage.tst
Of course...
select case substr(status,1,1) -- you're only interested in the first character.
when 'a' then 'Active'
when 'i' then 'Inactive'
when 't' then 'Terminated'
end as statustext
from stage.tst
However, there's a few worrying things about this schema. Firstly if you have a column that means something, appending a number onto the end it not necessarily the best way to go. Also, depending on the number of status' you have you might want to consider turning this column into a foreign key to a separate table.
Based on your comment you definitely want to turn this into a foreign key. For instance
create table statuses ( -- Not a good table name :-)
status varchar2(10)
, description varchar2(10)
, constraint pk_statuses primary key (status)
)
create table tst (
id number
, status varchar2(10)
, constraint pk_tst primary key (id)
, constraint fk_tst foreign key (status) references statuses (status)
)
Your query then becomes
select a.status, b.description
from tst a
left outer join statuses b
on a.status = b.status
Here's a SQL Fiddle to demonstrate.
It will be easier to do using decode.
SELECT
status,
decode ( status, 'a1','Active',
'a2','Active',
'a3','Active',
'i','Inactive',
't','Terminated',
'Default')STATUSTEXT
FROM STATUS
Since web search for Oracle case tops to that link, I add here for case statement, though not answer to the question asked about case expression:
CASE
WHEN grade = 'A' THEN dbms_output.put_line('Excellent');
WHEN grade = 'B' THEN dbms_output.put_line('Very Good');
WHEN grade = 'C' THEN dbms_output.put_line('Good');
WHEN grade = 'D' THEN dbms_output.put_line('Fair');
WHEN grade = 'F' THEN dbms_output.put_line('Poor');
ELSE dbms_output.put_line('No such grade');
END CASE;
or other variant:
CASE grade
WHEN 'A' THEN dbms_output.put_line('Excellent');
WHEN 'B' THEN dbms_output.put_line('Very Good');
WHEN 'C' THEN dbms_output.put_line('Good');
WHEN 'D' THEN dbms_output.put_line('Fair');
WHEN 'F' THEN dbms_output.put_line('Poor');
ELSE dbms_output.put_line('No such grade');
END CASE;
Per Oracle docs: https://docs.oracle.com/cd/B10501_01/appdev.920/a96624/04_struc.htm
Following syntax would work :
....
where x.p_NBR =to_number(substr(y.k_str,11,5))
and x.q_nbr =
(case
when instr(substr(y.m_str,11,9),'_') = 6 then to_number(substr(y.m_str,11,5))
when instr(substr(y.m_str,11,9),'_') = 0 then to_number(substr(y.m_str,11,9))
else
1
end
)
SELECT
STATUS,
CASE
WHEN STATUS IN('a1','a2','a3')
THEN 'Active'
WHEN STATUS = 'i'
THEN 'Inactive'
WHEN STATUS = 't'
THEN 'Terminated' ELSE null
END AS STATUSTEXT
FROM
stage.tst;
You can only check the first character of the status. For this you use substring function.
substr(status, 1,1)
In your case past.
DECODE(SUBSTR(STATUS,1,1),'a','Active','i','Inactive','t','Terminated','N/A')