How to optimize CASE statement in SELECT query which has same check condition for three different columns - oracle10g

I have below select statement to fetch the data having CASE statement.
SELECT PK_ID
,MGR_ID
,EMP_ID
,CASE
WHEN msts.MGR_ID is null AND msts.EMP_ID is not null THEN
(SELECT 'A' from dual)
ELSE
(SELECT 'B' from dual)
END FIRST_COL
,CASE
WHEN msts.MGR_ID is null AND msts.EMP_ID is not null THEN
(SELECT 'P' from dual)
ELSE
(SELECT 'Q' from dual)
END SECOND_COL
,CASE
WHEN msts.MGR_ID is null AND msts.EMP_ID is not null THEN
(SELECT 'X' from dual)
ELIE
(SELECT 'Y' from dual)
END THIRID_COL
from m_sel_tabs msts
here, As i know that we can check condition with mulitiple columns but the result will be only one for CASE statement.
so my question is here that as my check CASE statement is the same for all three columns (FIRST_COL, SECOND_COL, THIRID_COL), hence is there any other way to write this query in an optimized way.
Thanks in advance.

Maybe I'm wrong, but - when saying that you have "the same 3 CASE statements" - well, you don't. Those are 3 different CASE statements. An expression is the same, yes, but you're selecting 3 different columns, with different results (A, B, P, Q, X, Y - in your example).
You could create a function which would "hide" code you currently use and make the SELECT prettier, such as
select
pk_id, ...,
f_col(1, MGR_ID, EMP_ID) first_col,
f_col(2, MGR_ID, EMP_ID) second_col,
f_col(3, MGR_ID, EMP_ID) third_col
from m_sel_tabs
but - at the end - it would be just the same (or, possibly somewhat worse because of context switching) as you'd have to put all that code somewhere (into the function, right?).

Related

More Efficient Way to Join Three Tables Together in Postgres

I am attempting to link three tables together in postgres.
All three tables are generated from subqueries. The first table is linked to the second table by the variable call_sign as a FULL JOIN (because I want the superset of entries from both tables). The third table has an INNER JOIN with the second table also on call_sign (but theoretically could have been linked to the first table)
The query runs but is quite slow and I feel will become even slower as I add more data. I realize that there are certain things that I can do to speed things up - like not pulling unnecessary data in the subqueries and not converting text to numbers on the fly. But is there a better way to structure the JOINs between these three tables?
Any advice would be appreciated because I am a novice in postgres.
Here is the code:
select
(CASE
WHEN tmp1.frequency_assigned is NULL
THEN tmp2.lower_frequency
ELSE tmp1.frequency_assigned END) as master_frequency,
(CASE
WHEN tmp1.call_sign is NULL
THEN tmp2.call_sign
ELSE tmp1.call_sign END) as master_call_sign,
(CASE
WHEN tmp1.entity_type is NULL
THEN tmp2.entity_type
ELSE tmp1.entity_type END) as master_entity_type,
(CASE
WHEN tmp1.licensee_id is NULL
THEN tmp2.licensee_id
ELSE tmp1.licensee_id END) as master_licensee_id,
(CASE
WHEN tmp1.entity_name is NULL
THEN tmp2.entity_name
ELSE tmp1.entity_name END) as master_entity_name,
tmp3.market_name
FROM
(select cast(replace(frequency_assigned, ',','.') as decimal) AS frequency_assigned,
frequency_upper_band,
f.uls_file_number,
f.call_sign,
entity_type,
licensee_id,
entity_name
from combo_fr f INNER JOIN combo_en e
ON f.call_sign=e.call_sign
ORDER BY frequency_assigned DESC) tmp1
FULL JOIN
(select cast(replace(lower_frequency, ',','.') as decimal) AS lower_frequency,
upper_frequency,
e.uls_file_number,
mf.call_sign,
entity_type,
licensee_id,
entity_name
FROM market_mf mf INNER JOIN combo_en e
ON mf.call_sign=e.call_sign
ORDER BY lower_frequency DESC) tmp2
ON tmp1.call_sign=tmp2.call_sign
INNER JOIN
(select en.call_sign,
mk.market_name
FROM combo_mk mk
INNER JOIN combo_en en
ON mk.call_sign=en.call_sign) tmp3
ON tmp2.call_sign=tmp3.call_sign
ORDER BY master_frequency DESC;
you'll want to unwind those queries and do it all in one join, if you can. Soemthing like:
select <whatever you need>
from combo_fr f
JOIN combo_en e ON f.call_sign=e.call_sign
JOIN market_mf mf mf ON mf.call_sign=e.call_sign
JOIN combo_mk mk ON mk.call_sign=en.call_sign
I can't completely grok what you're doing, but some of the join clauses might have to become LEFT JOINs in order to deal with places where the call sign does or does not appear.
After creating indexes on call_sign for all four involved tables, try this:
WITH nodup AS (
SELECT call_sign FROM market_mf
EXCEPT SELECT call_sign FROM combo_fr
) SELECT
CAST(REPLACE(u.master_frequency_string, ',','.') AS DECIMAL)
AS master_frequency,
u.call_sign AS master_call_sign,
u.entity_type AS master_entity_type,
u.licensee_id AS master_licensee_id,
u.entity_name AS master_entity_name,
combo_mk.market_name
FROM (SELECT frequency_assigned AS master_frequency_string, call_sign,
entity_type, licensee_id, entity_name
FROM combo_fr
UNION ALL SELECT lower_frequency, call_sign,
entity_type, licensee_id, entity_name
FROM market_mf INNER JOIN nodup USING (call_sign)
) AS u
INNER JOIN combo_en USING (call_sign)
INNER JOIN combo_mk USING (call_sign)
ORDER BY 1 DESC;
I post this because this is the simplest way to understand what you need.
If there are no call_sign values which appear in both market_mf and
combo_fr, WITH nodup ... and INNER JOIN nodup ... can be omitted.
I am making the assumption that call_sign is unique in both combo_fr and market_mf ( = there are no two records in each table with the same value), even if there can be values which can appear in both tables.
It is very unfortunate that you order by a computed column, and that the computation is so silly. A certain optimization would be to convert the frequency strings once and for all in the table itself. The steps would be:
(1) add numeric frequncy columns to your tables (2) populate them with the values converted from the current text columns (3) convert new values directly into the new columns, by inputting them with a locale which has the desired decimal separator.

Sql Server - Running Totals Based on Conditions

I have been banging my head trying to come up with the correct logic (SQL Server 2012) needed to achieve something I would imagine would be fairly routine but I have been unable to find any examples of this anywhere. Basically, I have 3 columns in a table: product, flag, value. It is possible for a product to be listed multiple times within the table but only once with a unique flag (i.e. product1 can have flag1 or flag2 with different/identical but there will never be 2 records with product1 and flag1 and different/identical values).
The flag represents a pre-defined value (1,2,3,4) and the intention behind this field is to be able to assign a unique mathematical equation based on the value of the flag. The end result would yield a single product, the unique flag, and a new cumulative total based on the mathematical equation output. For instance, let's say product1 was listed 4 times with flag values of flag1, flag2, flag3, flag4 (see below):
Product-----Flag-----Value
Product1----Flag1----1.00
Product1----Flag2----3.00
Product1----Flag3----5.00
Product1----Flag4----7.00
Product-----Flag-----Value
Product1----Flag1----1.00 (flag1 value)
Product1----Flag2----4.00 (flag1+flag2 value)
Product1----Flag3----6.00 (flag1+flag3 value)
Product1----Flag4----10.00 (flag2+flag4 value)
Flag1 is defined as add flag1 only. Flag2 is defined as add flag1 and flag2. Flag 3 is defined as add flag1 and flag 3. Flag 4 is defined as add flag2 and flag4. the new output would be product1 listed four times with flag values of flag1, flag2, flag3, flag4 but new values as flag1, flag1_flag2, flag1+flag3, flag2+flag4.
I have tried to apply the logic via a case statement but I can't figure out how to traverse all the products for each condition and I have tried to go with a running totals solution but I am not sure how to incorporate the flag condition into it so it only performs a running total for when those conditions are true. Any assistance and/or article to help get me going down the right path would be greatly appreciated.
While I'm not sure I fully understand your question I think this might be what you want. For this to work it assumes flag1 is always present when flags 1 through 3 are and that flag2 is present when flag4 is.
;with cte as (
select
product,
max(case when flag = 'Flag1' then Value end) as f1Value,
max(case when flag = 'Flag2' then Value end) as f2Value,
max(case when flag = 'Flag3' then Value end) as f3Value,
max(case when flag = 'Flag4' then Value end) as f4Value
from flags group by Product
)
select
flags.Product,
flags.Flag,
flags.Value as "Org. value",
case flag
when 'Flag1' then f1Value
when 'Flag2' then f1Value + f2Value
when 'Flag3' then f1Value + f3Value
when 'Flag4' then f2Value + f4Value
else flags.Value -- take the present value when flag is not Flag1-4
end as "New value"
from flags
inner join cte on flags.Product = cte.Product
Take a look at this Sample SQL Fiddle to see it in action.
You can join a table to itself, and pick the conditions appropriately:
SELECT p1.product,p1.Flag,p1.Value + COALESCE(p2.Value,0)
FROM
Products p1
left join
Products p2
on
p1.Product = p2.Product and
p2.Flag = CASE p1.Flag
--1 doesn't need a previous value
WHEN 2 THEN 1
WHEN 3 THEN 1
WHEN 4 THEN 2
END
I assumed and tried on Range values.
CREATE TABLE #tmp (Product VARCHAR(10), flag VARCHAR(10),value numeric(13,2))
GO
INSERT INTO #tmp
SELECT 'Product1' , 'Flag1',1
UNION
SELECT 'Product1' , 'Flag2',3
UNION
SELECT 'Product1' , 'Flag3',5
UNION
SELECT 'Product1' , 'Flag4',7
GO
;WITH cte
AS
(
SELECT row_number () OVER(
ORDER BY flag) 'row',*
FROM #tmp
)
SELECT *,value 'RT'
FROM cte
WHERE row = 1
UNION
SELECT * ,(
SELECT cte.value
FROM cte
WHERE row = 1
) + value 'RT'
FROM cte
WHERE row BETWEEN 2
AND 3
UNION
SELECT * ,(
SELECT cte.value
FROM cte
WHERE row =2
) + value 'RT'
FROM cte
WHERE row >3
GO
DROP TABLE #tmp

Postgres - using select statement inside CASE WHEN

Inside case when condition I am executing select statement & if returns anything then I need to get it's value but I am getting error
ERROR: missing FROM-clause entry for table "us"
query is..
SELECT u.user_id,
CASE
WHEN
(SELECT us.attr_value
FROM user_setting us
WHERE us.user_id = u.user_id) IS NOT NULL THEN us.attr_value
ELSE
(SELECT gus.attr_value
FROM global_user_setting gus
WHERE gus.attr_key='key')
END
FROM user u
WHERE u.user_id IN (1,
2,3)
Error comes at IS NOT NULL THEN us.attr_value I understood the issue but couldn't find how to get that value outside select statement?
Try:
COALESCE((SELECT us.attr_value
FROM user_setting us
WHERE us.user_id = u.user_id),
(SELECT us.attr_value
FROM global_user_setting gs
WHERE gus.attr_key='key'))
instead. The reason for the problem is, that the binding of the es alias is not visible outside of the sub-select (as it is used in a "scalar" context). The whole subselect is basically a single expression, which will yield a single value.
Another (IMHO better) approach would be to left-join on the enrollment_settings table:
SELECT u.user_id,
COALESCE(us.attr_value, (SELECT gus.attr_value
FROM global_user_setting gs
WHERE gus.attr_key='key'))
FROM user u LEFT JOIN user_settings es ON us.user_id = u.user_id
WHERE u.user_id IN (1, 2, 3)
I assume here, that this join would yield at most a single row per row of user.

Using EXISTS as a column in TSQL

Is it possible to use the value of EXISTS as part of a query?
(Please note: unfortunately due to client constraints, I need SQLServer 2005 compatible answers!)
So when returning a set of results, one of the columns is a boolean value which states whether the subquery would return any rows.
For example, I want to return a list of usernames and whether a different table contains any rows for each user. The following is not syntactically correct, but hopefully gives you an idea of what I mean...
SELECT T1.[UserName],
(EXISTS (SELECT *
FROM [AnotherTable] T2
WHERE T1.[UserName] = T2.[UserName])
) AS [RowsExist]
FROM [UserTable] T1
Where the resultant set contains a column called [UserName] and boolean column called [RowsExist].
The obvious solution is to use a CASE, such as below, but I wondered if there was a better way of doing it...
SELECT T1.[UserName],
(CASE (SELECT COUNT(*)
FROM [AnotherTable] T2
WHERE T1.[UserName] = T2.[UserName]
)
WHEN 0 THEN CAST(0 AS BIT)
ELSE CAST(1 AS BIT) END
) AS [RowsExist]
FROM [UserTable] T1
Your second query isn't valid syntax.
SELECT T1.[UserName],
CASE
WHEN EXISTS (SELECT *
FROM [AnotherTable] T2
WHERE T1.[UserName] = T2.[UserName]) THEN CAST(1 AS BIT)
ELSE CAST(0 AS BIT)
END AS [RowsExist]
FROM [UserTable] T1
Is generally fine and will be implemented as a semi join.
The article Subqueries in CASE Expressions discusses this further.
In some cases a COUNT query can actually perform better though as discussed here
I like the other guys sql better but i just wrote this:
with bla as (
select t2.username, isPresent=CAST(1 AS BIT)
from t2
group by t2.username
)
select t1.*, isPresent = isnull(bla.isPresent, CAST(0 AS BIT))
from t1
left join blah on t1.username=blah.username
From what you wrote here I would alter your first query into something like this
SELECT
T1.[UserName], ISNULL(
(
SELECT
TOP 1 1
FROM [AnotherTable]
WHERE EXISTS
(
SELECT
1
FROM [AnotherTable] AS T2
WHERE T1.[UserName] = T2.[UserName]
)
), 0)
FROM [UserTable] T1
But actually if you use TOP 1 1 you would not need EXISTS, you could also write
SELECT
T1.[UserName], ISNULL(
(
SELECT
TOP 1 1
FROM [AnotherTable] AS T2
WHERE T1.[UserName] = T2.[UserName]
), 0)
FROM [UserTable] T1

using CASE in T-SQL in the where clause?

Im trying to use case to vary the value im checking in a where clause but I'm getting the error:
incorrect syntax near the keyword 'CASE'
SQL Server 2005
select *
from table
where ((CASE when adsl_order_id like '95037%'
then select '000000'+substring(adsl_order_id,6,6)
ELSE select adsl_order_id
END)
not in (select mwebID from tmp_csv_dawis_bruger0105)
Here is one way to include a case statement in a Where clause:
SELECT * FROM sometable
WHERE 1 = CASE WHEN somecondition THEN 1
WHEN someothercondition THEN 2
ELSE ... END
You could try
SELECT *
FROM table
WHERE (SELECT CASE WHEN adsl_order_id LIKE '95037%'
THEN '000000' + SUBSTRING(adsl_order_id, 6, 6)
ELSE adsl_order_id
END)
NOT IN (select mwebID from tmp_csv_dawis_bruger0105)
A correlated subquery is one possibility:
select *
from mytable
where not exists (
select *
from
tmp_csv_dawis_bruger0105
where
mwebID =
CASE when mytable.adsl_order_id like '95037%' then '000000' + substring(mytable.adsl_order_id,6,6)
ELSE mytable.adsl_order_id END
)
I came to this question looking for an answer thinking WHERE CASE ... would be the solution. I could not adapt the answers to my problem, but this technique to use a parameter that could be null, partial, or specific, works:
CREATE PROC myproc #vJobID VARCHAR (11)
AS
SELECT * FROM Jobs
WHERE Jobs.JobID like coalesce(#vJobID+'%','%')
HTH
You have one too many opening parentheses before the CASE expression.
Put it in the SELECT clause...
select *, (CASE when adsl_order_id like '95037%'
then '000000'+substring(adsl_order_id,6,6)
ELSE adsl_order_id
END) AS Id
from table
where not in (select mwebID from tmp_csv_dawis_bruger0105)
Also, you don't need to "SELECT" the result of the CASE.