SQL: Can I convert a string value of a "<" operator into an operator in a where clause? - sql

I am storing an operator in a string column for two rows of a table (">", "<=")
I am joining the table with another table and want to make the where clause as dynamic as possible.
I was wondering if it's possible to convert the string value operator into an actual operator for this line of SQL code:
ABS(DATEDIFF(dd,Table2.DUE_DT,GETDATE())) > 120
VS
ABS(DATEDIFF(dd,Table2.DUE_DT,GETDATE())) <= 120
The operator will change depending on matching columns in the row. Is it possible to change the operator based of the string value containing the correct operator? If so, how can this be done?
Below are the two rows from Table1
NEFL_TYPE GRGR_ID NEFL_KEY NEFL_VALUE NEFL_COLUMN
"PDRU" "2600" "PD" "RV" ">"
"PDRU" "2600" "RV" "PD" "<="
This is the snippet of code I use:
INNER JOIN
Table1
ON
Table2.STATUS = Table1.NEFL_KEY
AND
Table1.NEFL_TYPE = 'PDRU'
WHERE
Table1.GRGR_ID = '2600'
AND
ABS(DATEDIFF(dd, Table2.DUE_DT,GETDATE())) > 120
So Table2.STATUS should determine which operator to use in the NEFL_COLUMN

I don't think there's an easy way to do what you want for a general case, not even with dynamic queries or client-side generated queries, as the comparison is per row and performance would be an issue with dynamic queries.
I see 2 ways to solve the particular example case, though:
a) Make 2 separate queries and do a UNION on them
SELECT...
INNER JOIN
Table1
ON
Table2.STATUS = Table1.NEFL_KEY
AND
Table1.NEFL_TYPE = 'PDRU'
WHERE
Table1.GRGR_ID = '2600'
AND
ABS(DATEDIFF(dd, Table2.DUE_DT,GETDATE())) > 120 AND Table1.NEFL_COLUMN = ">"
UNION
SELECT...
INNER JOIN
Table1
ON
Table2.STATUS = Table1.NEFL_KEY
AND
Table1.NEFL_TYPE = 'PDRU'
WHERE
Table1.GRGR_ID = '2600'
AND
ABS(DATEDIFF(dd, Table2.DUE_DT,GETDATE())) <= 120 AND Table1.NEFL_COLUMN = "<="
b) Use 2 range columns instead of an "operator" column
NEFL_TYPE GRGR_ID NEFL_KEY NEFL_VALUE NEFL_COLUMN START END
"PDRU" "2600" "PD" "RV" ">" 121 9999
"PDRU" "2600" "RV" "PD" "<=" 0 120
--------------------------------------------------------------------------
INNER JOIN
Table1
ON
Table2.STATUS = Table1.NEFL_KEY
AND
Table1.NEFL_TYPE = 'PDRU'
WHERE
Table1.GRGR_ID = '2600'
AND
ABS(DATEDIFF(dd, Table2.DUE_DT,GETDATE())) BETWEEN Table1.START AND Table1.END
But none of them are as clean or elegant as I think you would like

Related

Pyspark - Passing inequality condition dynamically to dataframes join

I am using this code from another question: my question is how can I passing an inequality condition here for the join apart from the ON clause.
e.g my join condition is ("ID == ID") & ((DATE1 < DATE2) & (DATE3 > DATE4))
If my condition was only ID == ID, I am able to do that using list_of_join_columns = ['ID'] but I want to pass the inequality condition as well in the below code: please advise how can that be achieved.
*** existing code *****
def join_dataframes(list_of_join_columns, left_df, right_df):
return left_df.join(right_df, on=list_of_join_columns)
joined_df = functools.reduce(
functools.partial(join_dataframes, list_of_join_columns), list_of_dataframes,
)
You need to tag dataframe name along with the column in the join condition, in case the column name varies in both the dataframe
Also, it's adviced to rename the right dataframe common column such as id ,
right_df = right_df.withColumnRenamed("ID", "right_ID")
new_df = left_df.join(right_df, (left_df.ID == right_df.right_ID) & ((left_df.DATE1 < right_df.DATE2) & (left_df.DATE3 > right_df.DATE4))

Issue With SQL Pivot Function

I have a SQL query where I am trying to replace null results with zero. My code is producing an error
[1]: ORA-00923: FROM keyword not found where expected
I am using an Oracle Database.
Select service_sub_type_descr,
nvl('Single-occupancy',0) as 'Single-occupancy',
nvl('Multi-occupancy',0) as 'Multi-occupancy'
From
(select s.service_sub_type_descr as service_sub_type_descr, ch.claim_id,nvl(ci.item_paid_amt,0) as item_paid_amt
from table_1 ch, table_" ci, table_3 s, table_4 ppd
where ch.claim_id = ci.claim_id and ci.service_type_id = s.service_type_id
and ci.service_sub_type_id = s.service_sub_type_id and ch.policy_no = ppd.policy_no)
Pivot (
count(distinct claim_id), sum(item_paid_amt) as paid_amount For service_sub_type_descr IN ('Single-occupancy', 'Multi-occupancy')
)
This expression:
nvl('Single-occupancy',0) as 'Single-occupancy',
is using an Oracle bespoke function to say: If the value of the string Single-occupancy' is not null then return the number 0.
That logic doesn't really make sense. The string value is never null. And, the return value is sometimes a string and sometimes a number. This should generate a type-conversion error, because the first value cannot be converted to a number.
I think you intend:
coalesce("Single-occupancy", 0) as "Single-occupancy",
The double quotes are used to quote identifiers, so this refers to the column called Single-occupancy.
All that said, fix your data model. Don't have identifiers that need to be quoted. You might not have control in the source data but you definitely have control within your query:
coalesce("Single-occupancy", 0) as Single_occupancy,
EDIT:
Just write the query using conditional aggregation and proper JOINs:
select s.service_sub_type_descr, ch.claim_id,
sum(case when service_sub_type_descr = 'Single-occupancy' then item_paid_amt else 0 end) as single_occupancy,
sum(case when service_sub_type_descr = 'Multi-occupancy' then item_paid_amt else 0 end) as multi_occupancy
from table_1 ch join
table_" ci
on ch.claim_id = ci.claim_id join
table_3 s
on ci.service_type_id = s.service_type_id join
table_4 ppd
on ch.policy_no = ppd.policy_no
group by s.service_sub_type_descr, ch.claim_id;
Much simpler in my opinion.
for column aliases, you have to use double quotes !
don't use
as 'Single-occupancy'
but :
as "Single-occupancy",

Oracle Query is timing out

I'm trying to write an Oracle query to join data from 4 different tables. The code is below:
SELECT
PROJ.PRJ_NO, PROJ.PRJ_NAME, PROJ.PRJ_BEG_DATE, PROJ.PRJ_END_DATE, PORT.TIER1_NAME, PORT.TIER2_NAME, PORT.TIER3_NAME, MAX(A.FIS_WK_END_DATE) AS "FISCAL_WEEK", SUM(A.ABDOL) AS "AAB_DOL", SUM(A.VHDOL) AS "AVH_DOL", SUM(A.ADOL) AS "AA_DOL", SUM(A.DCDOL) AS "ADC_DOL", SUM(A.DCGADOL) AS "ADC_GA_DOL", SUM(A.COM) AS "AM_DOL", SUM(A.FE) AS "AFE_DOL", SUM(A.IE) AS "AIE_DOL", SUM(A.OTHER) AS "AR_DOL", SUM(A.MTSFT) AS "AS_FT", SUM(A.MTSST) AS "AS_ST", SUM(A.ACTST) AS "AL_ST", SUM(A.ACTFT) AS "ALL_FT", MAX(P.SNAPSHOT_DATE) as "SNAP_DATE", P.FINSCN_TYPE, SUM(P.ABDOL) AS "PAB_DOL", SUM(P.VHDOL) AS "PVH_DOL", SUM(P.DCDOL) AS "PDC_DOL", SUM(P.TCI_DOL) AS "PCI_GA_DOL", SUM(P.GADOL) AS "PN_GA_DOL", SUM(P.COM) AS "PN_COM", SUM(P.FEE) AS "PN_FEE", SUM(P.D_IE) AS "PN_MOIE", SUM(P.OTHER) AS "PN_OTHER"
FROM PROJ_TASK_VW PROJ
LEFT JOIN PORTFOLIO_VW PORT
ON PROJ.TASKNO = PORT.TASKNO
LEFT JOIN ACTUAL_VW A
ON PROJ.TASKNO = A.CURR_TASKNO
LEFT JOIN BUDG_DOLL_VW P
ON PROJ.TASKNO = P.CURR_TASKNO
WHERE TO_CHAR(PROJ.PRJ_END_DATE, 'YYYY-MM-DD') > '2018-10-01'
AND PROJ.P_FLAG = 'N'
AND (PROJ.P_TYPE LIKE 'D-%' OR PROJ.P_TYPE LIKE '%MR%' OR PROJ.P_TYPE LIKE '%ID%')
AND (SUBSTR(PROJ.PRJ_NO,3,3) != 'BP' AND SUBSTR(PROJ.PRJ_NO,3,3) != 'PJ')
AND (P.FINSCN_TYPE = 'SR' OR P.FINSCN_TYPE = 'BUG')
AND (A.ABDOL + A.VHDOL + A.ADOL + A.DCDOL + A.DCGADOL + A.COM +
A.FE + A.IE + A.OTHER) <> 0
GROUP BY
PROJ.PRJ_NO,
PROJ.PRJ_NAME,
PROJ.PRJ_BEG_DATE,
PROJ.PRJ_END_DATE,
PORT.TIER1_NAME,
PORT.TIER2_NAME,
PORT.TIER3_NAME,
P.FINSCN_TYPE
My overall intent is to bring all of the select fields into a single table using left joins (using table "PROJ" as the parent table and the remaining tables providing child data based on the data returned from the "PROJ" table. When the query is ran it times out after about 30mins. Is there a better way to write this query to where I can build the table I need without timing out???
First, there's no way to answer this question without an execution plan. What columns do you have indexed? But here are some things I noticed.
WHERE TO_CHAR(PROJ.PRJ_END_DATE, 'YYYY-MM-DD') > '2018-10-01'
Your column is a date, so you should be comparing to a date, rather than converting to a VARCHAR2 and doing an inequality on strings.
AND (PROJ.P_TYPE LIKE 'D-%' OR PROJ.P_TYPE LIKE '%MR%' OR PROJ.P_TYPE LIKE '%ID%')
I'm not sure, but these will likely not be very performant because of the wildcards. Indexes might make these better, but I never remember how wildcard searches work with indexes.
AND (SUBSTR(PROJ.PRJ_NO,3,3) != 'BP' AND SUBSTR(PROJ.PRJ_NO,3,3) != 'PJ')
These do nothing since your two SUBSTRs return strings of 3 characters long and you are comparing them to 2 character long strings.
AND (A.ABDOL + A.VHDOL + A.ADOL + A.DCDOL + A.DCGADOL + A.COM + A.FE + A.IE + A.OTHER) <> 0
Do you actually care about the sum here, or are you just checking that one or more of these values is non-zero. If these values are always > 0, then you're better off replacing this with:
AND ( a.ABDOL > 0 OR A.VHDOL > 0 ...

How to give change working of having function dynamicaly on executing an sql statement?

I'm having a Sql code like as follows
Select a.ItemCode, a.ItemDesc
From fn_BOM_Material_Master('A', #AsOnDate, #RptDate, #BranchID, #CompID)a
Left Outer Join fn_INV_AsOnDate_Stock(#StockDate, #AsOnDate, #RptDate, #BranchID, #CompID, #Finyear)b
On a.ItemCode=b.ItemCode and b.WarehouseCode<>'WAP'
and a.BranchID=b.BranchID and a.CompID=b.COmpID
Where a.ItemNatureCode = 'F' and a.BranchID = #BranchID and a.CompID = #CompID
Group by a.ItemCode, a.ItemDesc
Having sum(b.CBQty)<=0
Here the problem is that im passing an "#ShowZeroStock" value as as bit if the "#ShowZeroStock" value is '1' then Having should not be validated or (i.e: All values from the table should be returned including zero)
So How to change the query based on passed bit value "#ShowZeroStock"
I can Use "If else " condition at the top and remove having in else part, but for a lengthy query i can't do the same.
Is this the logic you want?
Having sum(b.CBQty) <= 0 or #ShowZeroStock = 1

Conditional Sql in Daisy chained Query

I have one master table with all the IDs to each child table. The SQL statement looks like this...
SELECT Class.Descript
, Regulation.Descript AS Reg
, Compgroup.Descript AS Grouping
, Category.Descript AS Cat
, Exempt.Descript AS Exempt
, Reason.Descript AS Reasons
, COALESCE(ComponentRuleSet.NormalType, ComponentRuleSet.Supertype, '') AS Type
FROM ComponentRuleSet
LEFT OUTER JOIN Reason
ON ComponentRuleSet.ComponentCategoryID = Reason.ComponentCategoryID
LEFT OUTER JOIN Class
ON ComponentRuleSet.ComponentClassID = Class.ComponentClassID
LEFT OUTER JOIN Regulation
ON ComponentRuleSet.RegulationID = Regulation.RegulationID
LEFT OUTER JOIN Compgroup
ON ComponentRuleSet.ComplianceGroupID = Compgroup.ComplianceGroupID
LEFT OUTER JOIN Category
ON ComponentRuleSet.ComponentCategoryID = Category.ComponentCategoryId
LEFT OUTER JOIN Exempt
ON ComponentRuleSet.ExemptID = Exempt.ComponentExemptionID
WHERE (ComponentRuleSet.ComponentID = 38048)
The problem is that there are two fields in the ComponentRuleSet table called NormalType and Supertype. If either of those fields have a value, I need to display it in a column called Type. Yet, if neither have a value I need to display a Blank value in the Type column.
Any ideas?
---EDIT
Is my placement of COALESCE correct in the edited query? It is still returning errors.
--UPDATE
IMPORTANT: The type of both fields are boolean, I need to return the column name of the column that holds a TRUE value, and place that value in the TYPE column.
Use COALESCE for this field:
COALESCE(ComponentRuleSet.NormalType, ComponentRuleSet.Supertype, '') AS Type
COALESCE:
Returns the first nonnull expression among its arguments.
Following your comments as to the actual requirement, CASE is probably a better option:
CASE WHEN ComponentRuleSet.NormalType = 1 THEN 'NormalType'
WHEN ComponentRuleSet.Supertype = 1 THEN 'SuperType'
ELSE ''
END AS Type
Seeing your comments, perhaps a CASE expression will work:
select ...
, CASE WHEN ComponentRuleSet.NormalType is not null then 'NormalType'
WHEN ComponentRuleSet.Supertype is not null then 'SuperType'
ELSE ''
end as Type
UPDATE Since boolean values are just 1 for true and 0 for false, try this:
select ...
, CASE WHEN ComponentRuleSet.NormalType = 1 then 'NormalType'
WHEN ComponentRuleSet.Supertype = 1 then 'SuperType'
ELSE ''
end as Type