Stored Procedure with composite Order by - sql

I am creating a stored procedure like this :
select *
from INVENTORYTABLE inv
join INVENTORYTABLE_New invNew on inv.Stock_ID = invNew.Stock_ID
where
(
(AvailabilityStatus ='A' Or AvailabilityStatus = 'B' Or AvailabilityStatus = 'D' Or AvailabilityStatus = 'H' or AvailabilityStatus='E' ) And
MediaCount>0
)
I want to sort the results by stockId for Availability status 'A' to 'H' and then for 'E'.
Is this possible?
What I want is to show results from A to H ordered by StockId and then E below the ordered by stock Id.

You may implement an ORDER BY clause using a CASE expression:
ORDER BY
CASE WHEN AvailabilityStatus = 'E' THEN 1 ELSE 0 END,
stockId

select *
from INVENTORYTABLE inv
join INVENTORYTABLE_New invNew on inv.Stock_ID = invNew.Stock_ID
where
(
(AvailabilityStatus ='A' Or AvailabilityStatus = 'B' Or AvailabilityStatus = 'D' Or AvailabilityStatus = 'H' or AvailabilityStatus='E' ) And
MediaCount>0
)
order by
case
when AvailabilityStatus ='A' then 1
when AvailabilityStatus ='B' then 2
when AvailabilityStatus ='D' then 3
when AvailabilityStatus = 'H' then 4
when AvailabilityStatus='E' then 5
else 100
end
You can try something like this

Related

Need help to simplify this T-SQL query

I have this working T-SQL query, is there any way to simplify it?
The SELECT COUNT(*) FROM Tablename is repeating and only the condition is different for each query, so is there a way to simplify this more? thank you
SELECT COUNT(*) FROM Table
WHERE columnA = 'I' AND columnB = 'I' AND columnC =''
UNION ALL
SELECT COUNT(*) FROM Table
WHERE columnA = 'I' AND columnB = 'I' AND columnC <>''
UNION ALL
SELECT COUNT(*) FROM Table
WHERE columnA IN ('A','R') AND columnB = 'I' AND columnD IN (SELECT columnE FROM Table2)
UNION ALL
SELECT COUNT(*) FROM Table
WHERE columnA IN ('B') AND columnB = 'I'
UNION ALL
SELECT COUNT(*) FROM Table
WHERE columnA IN ('R') AND columnB = 'S'
UNION ALL
SELECT COUNT(*) FROM Table
WHERE columnA IN ('A') AND columnB = 'S'
This is the working query and my output is 6 rows, want to maintain that
Let me assume that BI_BRH is unique in STDHOST. This is not strictly necessary but it are simplifying.
Then you can use conditional aggregation. I think putting the values in columns is the simplest approach:
SELECT SUM(CASE WHEN STATUS = 'I' AND TRX_STATE = 'I' AND ASSIGN_TO = '' THEN 1 ELSE 0 END),
SUM(CASE WHEN STATUS = 'I' AND TRX_STATE = 'I' AND ASSIGN_TO <>'' THEN 1 ELSE 0 END)
SUM(CASE WHEN STATUS IN ('A','R') AND TRX_STATE = 'I' AND s.BI_BRH IS NOT NULL THEN 1 ELSE 0 END)
SUM(CASE WHEN STATUS IN ('B') AND TRX_STATE = 'I' THEN 1 ELSE 0 END)
SUM(CASE WHEN STATUS IN ('R') AND TRX_STATE = 'S' THEN 1 ELSE 0 END)
SUM(CASE WHEN STATUS IN ('A') AND TRX_STATE = 'S' THEN 1 ELSE 0 END)
FROM ICSCHQINFO i LEFT JOIN
STDHOST s
ON s.BI_BRH = i.BRH_ASSIGN_TO;
Your code runs 6 times over the same table, with different conditions in the WHERE clause and returns 6 rows.
Assuming that the 6 conditions you have are mutually exclusive, you can create a CTE that returns for each row the category that it belongs to and then aggregate on that category:
WITH cte AS (
SELECT CASE
WHEN STATUS = 'I' AND TRX_STATE = 'I' AND ASSIGN_TO = '' THEN 'cat1'
WHEN STATUS = 'I' AND TRX_STATE = 'I' AND ASSIGN_TO <> '' THEN 'cat2'
WHEN STATUS IN ('A','R') AND TRX_STATE = 'I' AND BRH_ASSIGN_TO IN (SELECT BI_BRH FROM STDHOST) THEN 'cat3'
WHEN STATUS IN ('B') AND TRX_STATE = 'I' THEN 'cat4'
WHEN STATUS IN ('R') AND TRX_STATE = 'S' THEN 'cat5'
WHEN STATUS IN ('A') AND TRX_STATE = 'S' THEN 'cat6'
END category
FROM ICSCHQINFO
)
SELECT category, COUNT(*) counter
FROM cte
GROUP BY category
If there are no rows for any of the categories and in that case you want a row with 0, you need another CTE that returns the categories and LEFT join to the other CTE:
WITH
categories AS (SELECT category FROM (VALUES ('cat1'), ('cat2'), ('cat3'), ('cat4'), ('cat5'), ('cat6')) v(category)),
cte AS (
SELECT CASE
WHEN STATUS = 'I' AND TRX_STATE = 'I' AND ASSIGN_TO = '' THEN 'cat1'
WHEN STATUS = 'I' AND TRX_STATE = 'I' AND ASSIGN_TO <> '' THEN 'cat2'
WHEN STATUS IN ('A','R') AND TRX_STATE = 'I' AND BRH_ASSIGN_TO IN (SELECT BI_BRH FROM STDHOST) THEN 'cat3'
WHEN STATUS IN ('B') AND TRX_STATE = 'I' THEN 'cat4'
WHEN STATUS IN ('R') AND TRX_STATE = 'S' THEN 'cat5'
WHEN STATUS IN ('A') AND TRX_STATE = 'S' THEN 'cat6'
END category
FROM ICSCHQINFO
)
SELECT c.category, COUNT(t.category) counter
FROM categories c LEFT JOIN cte t
ON t.category = c.category
GROUP BY c.category

Want to Return Empty Rows With a Case When Statement

Lets say I'm using 2 case when statements to group my data, like in the below example:
select case
when group1 = 'A' then 'Large'
when group1 = 'B' then 'Medium'
else 'Small'
end as 'Order Size'
,case
when method = 'Delivery' then 'Delivery'
else 'Pick-up'
end as 'Distribution Method'
,count(distinct(OrderIDs))
from OrderTable
GROUP BY
select case
when group1 = 'A' then 'Large'
when group1 = 'B' then 'Medium'
else 'Small'
end
,case
when method = 'Delivery' then 'Delivery'
else 'Pick-up'
end
Lets also say that there were no "Large" deliveries that were "Pick-Up'. Currently, this query will not return a row with Large,PickUp category.
Is there a way to have a row returned with 0’s if there is nothing that meets the multiple case when criteria?
Use a cross join to generate the rows and left join to bring in the data:
select os.OrderSize, coalesce(d.DistributionMethod, 'Pick-Up') as
count(*)
from (select 'Large' as OrderSize union all
select 'Medium' as OrderSize union all
select 'Small' as OrderSize
) os cross join
(select 'Delivery' as DistributionMethod union all
select 'Pick-Up' as DistributionMethod
) d left join
OrderTable ot
on ( (ot.group1 = 'A' and os.OrderSize = 'Large') or
(ot.group1 = 'B' and os.OrderSize = 'Medium') or
(ot.group1 not in ('A', 'B') and os.OrderSize = 'Small')
) and
ot.method = d.DistributionMethod
group by os.OrderSize, coalesce(d.DistributionMethod, 'Pick-Up');
Not all databases support the creation of a table of constants using this syntax, but there is generally some syntax that does this.
You could select a recordset that contains the required values and then left join your grouped recordset from there. Following is an example for SQL Server where you would join your results to [Groupings].[OrderSize] and [Groupings].[DistributionMethod]:
SELECT *
FROM (
SELECT *
FROM (
SELECT 'Large' AS [OrderSize]
UNION
SELECT 'Medium' AS [OrderSize]
UNION
SELECT 'Small' AS [OrderSize]
) AS [OrderSizes]
CROSS JOIN (
SELECT 'Delivery' AS [DistributionMethod]
UNION
SELECT 'Pick-up' AS [DistributionMethod]
) AS [DistributionMethods]
) AS [Groupings]
LEFT JOIN ...

case statement to find one value

I'm trying to show one row per id but it is returning three.
If the id has a 'y' then it should show a 'y'.
If it shows a 'y' and 'r' it should be 'y'.
If it has 'y', 'r', 'n' it should be 'y'.
If it is just id and 'r' it should be 'r' and id and just 'n' then 'n'.
I can't seem to get it to work using a case statement. Any ideas? Thanks.
I've tried this:
,CASE WHEN result = 'Y' THEN 'Y'
WHEN result = 'Y' AND result = 'R') THEN 'Y'
WHEN result = 'R' THEN 'R'
ELSE 'N' END AS CARE_PLAN
What it is returning:
ID result
3434 'y'
3434 'r'
3434 'n'
You can use Listagg function,
Writing a subquery and DISTINCT then use Listagg function.
SELECT id, Listagg (result, ', ')
within GROUP (ORDER BY result) as CARE_PLAN
FROM (SELECT DISTINCT id,
( CASE
WHEN result = 'Y' THEN 'Y'
WHEN result = 'Y'
AND result = 'R' THEN 'Y'
WHEN result = 'R' THEN 'R'
ELSE 'N'
END ) AS result
FROM t) T
GROUP BY id
sqlfiddle:http://sqlfiddle.com/#!4/02cd5/2
[Results]:
| ID | CARE_PLAN |
|------|-----------|
| 1234 | N, R, Y |
It shall be proper to use ASCII and CHR functions for your case instead of using CASE .. WHEN, as in the following :
SELECT ID, CHR(MAX(ASCII(result))) AS CARE_PLAN
FROM TAB
GROUP BY ID
ORDER BY ID;
SQL Fiddle Demo
You would seem to want aggregation with some conditional logic:
select id,
(case when sum(case when result = 'y' then 1 else 0 end) > 0 then 'y'
when sum(case when result = 'r' then 1 else 0 end) > 0 then 'r'
when min(result) = max(result) and min(result) = 'n' then 'n'
else '?'
end) as new_result
from t
group by id;
If there are only those three values, then perhaps this simplified logic works:
select id, max(result) as new_result
from t
group by id;

counting records on the same table with different values possibly none sql server 2008

I have a inventory table with a condition i.e. new, used, other, and i am query a small set of this data, and there is a possibility that all the record set contains only 1 or all the conditions. I tried using a case statement, but if one of the conditions isn't found nothing for that condition returned, and I need it to return 0
This is what I've tried so far:
select(
case
when new_used = 'N' then 'new'
when new_used = 'U' then 'used'
when new_used = 'O' then 'other'
end
)as conditions,
count(*) as count
from myDB
where something = something
group by(
case
when New_Used = 'N' then 'new'
when New_Used = 'U' then 'used'
when New_Used = 'O' then 'other'
end
)
This returns the data like:
conditions | count
------------------
new 10
used 45
I am trying to get the data to return like the following:
conditions | count
------------------
new | 10
used | 45
other | 0
Thanks in advance
;WITH constants(letter,word) AS
(
SELECT l,w FROM (VALUES('N','new'),('U','used'),('O','other')) AS x(l,w)
)
SELECT
conditions = c.word,
[count] = COUNT(x.new_used)
FROM constants AS c
LEFT OUTER JOIN dbo.myDB AS x
ON c.letter = x.new_used
AND something = something
GROUP BY c.word;
try this -
DECLARE #t TABLE (new_used CHAR(1))
INSERT INTO #t (new_used)
SELECT t = 'N'
UNION ALL
SELECT 'N'
UNION ALL
SELECT 'U'
SELECT conditions, ISNULL(r.cnt, 0) AS [count]
FROM (
VALUES('U', 'used'), ('N', 'new'), ('O', 'other')
) t(c, conditions)
LEFT JOIN (
SELECT new_used, COUNT(1) AS cnt
FROM #t
--WHERE something = something
GROUP BY new_used
) r ON r.new_used = t.c
in output -
new 2
used 1
other 0
You can do it as a cross-tab:
select
sum(case when new_used = 'N' then 1 else 0 end) as N,
sum(case when new_used = 'U' then 1 else 0 end) as U,
sum(case when new_used = 'O' then 1 else 0 end) as Other
from myDB
where something = something

Query Optimization in oracle

I need to fetch result from one tables and one view joined on 2 columns.
View: 8million records
Table: 5K Records
I went through query plan and observe that query will take very long to run and infact try to run this query but not getting any result.
Please help me in optimize the query.I am not using any hint.
SELECT coupon_upc
, sum ( loyalty_a ) a
, sum ( loyalty_b ) b
, sum ( loyalty_c ) c
, sum ( loyalty_x )
FROM ( SELECT ccd.coupon_upc AS coupon_upc
, ( CASE WHEN a.loyalty_cell = 'A' then 1 else 0 end ) AS loyalty_a
, ( CASE WHEN a.loyalty_cell = 'B' then 1 else 0 end ) AS loyalty_b
, ( CASE WHEN a.loyalty_cell = 'C1' then 1 else 0 end ) AS loyalty_c
, ( CASE WHEN a.loyalty_cell = 'X' then 1 else 0 end ) AS loyalty_x
FROM view1 a
, ( select distinct coupon_upc
, coupon_id
, division
from table2
where schedule_key = 'XXX' ) ccd
WHERE a.campaign_code = 'XXX'
AND a.coupon_id = ccd.coupon_id
AND a.division = ccd.division ) a
GROUP BY coupon_upc
Without the explain plan, or the schema/DDL, there is a limitted amount of optimisation that can be done.
Here is an alternative, but you'd need to test it to see if it makes any difference. (Replace a join with a correlated sub-query.)
SELECT
coupon_upc, sum(loyalty_a) a, sum(loyalty_b) b, sum(loyalty_c) c, sum(loyalty_x) x
FROM
(
SELECT
(
SELECT
coupon_upc
FROM
table2
WHERE
schedule_key = 'XXX'
AND coupon_id = a.coupon_id
AND division = a.division
GROUP BY
coupon_upc
) as coupon_upc,
(case when a.loyalty_cell = 'A' then 1 else 0 end) as loyalty_a,
(case when a.loyalty_cell = 'B' then 1 else 0 end) as loyalty_b,
(case when a.loyalty_cell = 'C1' then 1 else 0 end) as loyalty_c,
(case when a.loyalty_cell = 'X' then 1 else 0 end) as loyalty_x
FROM
view1 a
WHERE
a.campaign_code = 'XXX'
) a
GROUP BY
coupon_upc
Other than that, the kind of optimisations are:
- persisting the view
- indexes
- refactoring data structures
EDIT
Another possible refactor of the query... I don't know how well Oracle would optimise the 4 instances of correlated sub-queries.
SELECT
coupon_upc,
SUM((SELECT COUNT(*) FROM view1 WHERE campaign_code = 'XXX' AND loyalty_cell = 'A' AND coupon_id = map.coupon_id AND division = map.division)) AS loyalty_a,
SUM((SELECT COUNT(*) FROM view1 WHERE campaign_code = 'XXX' AND loyalty_cell = 'B' AND coupon_id = map.coupon_id AND division = map.division)) AS loyalty_b,
SUM((SELECT COUNT(*) FROM view1 WHERE campaign_code = 'XXX' AND loyalty_cell = 'C1' AND coupon_id = map.coupon_id AND division = map.division)) AS loyalty_c,
SUM((SELECT COUNT(*) FROM view1 WHERE campaign_code = 'XXX' AND loyalty_cell = 'X' AND coupon_id = map.coupon_id AND division = map.division)) AS loyalty_x
FROM
(
SELECT coupon_upc, coupon_id, division
FROM table2 WHERE schedule_key = 'xxx'
GROUP BY coupon_upc, coupon_id, division
)
AS map
GROUP BY
coupon_upc
Or maybe...
SELECT
map.coupon_upc, SUM(data.loyalty_a) AS a, SUM(data.loyalty_b) AS b, SUM(data.loyalty_c) AS c, SUM(data.loyalty_x) AS X
FROM
(
SELECT coupon_upc, coupon_id, division
FROM table2 WHERE schedule_key = 'xxx'
GROUP BY coupon_upc, coupon_id, division
)
AS map
INNER JOIN
(
SELECT
coupon_id,
division,
SUM(CASE WHEN loyalty_cell = 'A' THEN 1 ELSE 0 END) AS loyalty_a,
SUM(CASE WHEN loyalty_cell = 'B' THEN 1 ELSE 0 END) AS loyalty_b,
SUM(CASE WHEN loyalty_cell = 'C1' THEN 1 ELSE 0 END) AS loyalty_c,
SUM(CASE WHEN loyalty_cell = 'X' THEN 1 ELSE 0 END) AS loyalty_x
FROM
view1
WHERE
campaign_code = 'XXX'
)
AS data
ON data.coupon_id = map.coupon_id
AND data.division = map.division
GROUP BY
map.coupon_upc
Another possible rewrite would be:
SELECT
map.coupon_upc
, COUNT(CASE WHEN a.loyalty_cell = 'A' THEN 1 ELSE NULL END) AS loyalty_a
, COUNT(CASE WHEN a.loyalty_cell = 'B' THEN 1 ELSE NULL END) AS loyalty_b
, COUNT(CASE WHEN a.loyalty_cell = 'C1' THEN 1 ELSE NULL END) AS loyalty_c
, COUNT(CASE WHEN a.loyalty_cell = 'X' THEN 1 ELSE NULL END) AS loyalty_x
FROM
( SELECT coupon_upc, coupon_id, division
FROM table2 WHERE schedule_key = 'xxx'
GROUP BY coupon_upc, coupon_id, division
) AS map
JOIN view1 a
ON a.coupon_id = map.coupon_id
AND a.division = map.division
WHERE
a.campaign_code = 'xxx'
GROUP BY
map.coupon_upc
Do you have indexes on the fields that are used in the JOIN, WHERE and the GROUP BY clauses?