在 SQL 中动态构建 WHERE 子句,无需硬解析即可执行
Posted
技术标签:
【中文标题】在 SQL 中动态构建 WHERE 子句,无需硬解析即可执行【英文标题】:Build WHERE clause in SQL dynamically and execute without hard parsing 【发布时间】:2016-04-15 15:59:47 【问题描述】:我想动态构建 SQL,添加/更改/删除 WHERE 条件,直到找到记录。 添加/更改/删除 WHERE 条件基于业务规则设置的优先级。
为了更好地描述问题陈述,这是我的 2 个结构表(示例键列)
表 1:--> 此表包含每个提交的索赔的索赔相关信息。
CLAIM_DOCUMENT
--------------
(
HDR_SID NUMBER,
PRVDR_NPI NUMBER,
INV_TYPE VARCHAR2(4),
SPLTY VARCHAR2(100),
PRCDR_CODE VARCHAR2(100),
MDFR_CODE VARCHAR2(10),
DIAG_CODE VARCHAR2(100) )
表 2:--> 这是保存提供商费率的配置表。
PROVIDER_RATE
-------------
( PRVDR_SID NUMBER,
PRVDR_NPI NUMBER,
SPLTY VARCHAR2(100),
SUB_SPLTY VARCHAR2(100),
PRCDR_CODE VARCHAR2(100),
MDFR_CODE VARCHAR2(10)
RATE_VALUE NUMBER(20,6)
RATE_TYPE VARCHAR2(10)
);
目标是从 PROVIDER_RATE 表中确定适用的 RATE_VALUE。 为了确定提供者费率,我们将在声明中提交的属性(即 PRVDR_NPI、PRCDR_CODE、MDFR_CODE)与 PROVIDER_RATE 匹配以找到 RATE_VALUE。
声明中的所有属性可能与 PROVIDER_RATE 不匹配,因此我们添加/更改/删除“WHERE”条件,直到从 PROVIDER_RATE 表中找到 RATE_VALUE。
例如在第一遍中,业务规则说匹配所有可能的值,所以我编写 SQL 语句如下:
SELECT RATE_VALUE, RATE_TYPE into v_rate_value, v_rate_type
FROM CLAIM_DOCUMENT cd, PROVIDER_RATE pr
WHERE cd.hdr_sid = p_hdr_sid -- p_hdr_sid is passed as parameter to procedure for 1 claim
AND cd.PRVDR_NPI = pr.PRVDR_NPI -- PRVDR_NPI is key column to match between 2 tables
----all optional where clause to form here based on busienss rules priority --
AND cd.PRCDR_CODE = pr.PRCDR_CODE -- optional WHERE clause 1
AND cd.MDFR_CODE = pr.MDFR_CODE -- optional WHERE clause 2
AND cd.SPLTY = pr.SPLTY -- optional WHERE clause 3
--IF rate value is NOT found using all WHERE clause above, the next rule priority
--is to REMOVE SPLTY and check
IF v_rate_value IS NULL THEN
SELECT RATE_VALUE, RATE_TYPE into v_rate_value, v_rate_type
FROM CLAIM_DOCUMENT cd, PROVIDER_RATE pr
WHERE cd.hdr_sid = p_hdr_sid -- p_hdr_sid is passed as parameter to procedure for 1 claim
AND cd.PRVDR_NPI = pr.PRVDR_NPI -- PRVDR_NPI is key column to match between 2 tables
----all optional where clause to form here based on busienss rules priority --
AND cd.PRCDR_CODE = pr.PRCDR_CODE -- optional WHERE clause 1
AND cd.MDFR_CODE = pr.MDFR_CODE -- optional WHERE clause 2
END IF;
--IF rate value is NOT found using WHERE clause above,
--the next rule priority is to REMOVE MDFR_CODE and check
IF v_rate_value IS NULL THEN
SELECT RATE_VALUE, RATE_TYPE into v_rate_value, v_rate_type
FROM CLAIM_DOCUMENT cd, PROVIDER_RATE pr
WHERE cd.hdr_sid = p_hdr_sid -- p_hdr_sid is passed as parameter to procedure for 1 claim
AND cd.PRVDR_NPI = pr.PRVDR_NPI -- PRVDR_NPI is key column to match between 2 tables
----all optional where clause to form here based on busienss rules priority --
AND cd.PRCDR_CODE = pr.PRCDR_CODE -- optional WHERE clause 1
END IF;
我的问题: 有没有办法通过读取表中配置的业务规则来动态构建 WHERE 子句,更重要的是执行 DYNAMIC 形成的 SQL 语句没有硬解析?
【问题讨论】:
当然,只需阅读业务规则,将它们放在variable varchar2(100)
和EXECUTE IMMEDIATE variable
中
查询会有多少种变体?如果只有 3 个,那么硬解析并不重要。
我不想使用 EXECUTE IMMEDIATE 因为这个过程是最常用的,我相信这会产生性能问题。可能有很多变化,所以我想让它更具可配置性和动态性,但不以牺牲性能为代价
只有一个提示:如果查询返回零或多行,使用SELECT ... INTO ...
会引发异常,因此这种精确的查询语法对您不起作用。
【参考方案1】:
这样有条件地打开/关闭条件是否可以接受:
AND (cd.PRCDR_CODE = pr.PRCDR_CODE OR l_prcdr_code_flag='off') -- optional WHERE clause 1
AND (cd.MDFR_CODE = pr.MDFR_CODE OR l_mdfr_code_flag='off') -- optional WHERE clause 2
AND (cd.SPLTY = pr.SPLTY OR l_splty_flag='off') -- optional WHERE clause 3
您在其余代码中所要做的就是处理“标志”,即。在开始时将所有l_*_flags
变量设置为'on'
,然后随时关闭它们(将它们设置为'off'
)。
这样您就不会更改 SQL,因此不需要硬解析。
为您的查询创建一个游标变量以避免重复代码也是一个好主意。
【讨论】:
首先,感谢 GoranM。不知道我是否做对了,所以我想问一下这种情况。如果我完全不必根据业务要求考虑 MDFR_CODE 和 SPLTY ,那么 flag 将如何提供帮助,例如假设我必须执行以下操作,您能否向我提供带有标志的查询: AND (cd.PRCDR_CODE = pr.PRCDR_CODE OR l_prcdr_code_flag='off') -- 可选 WHERE 子句 1【参考方案2】:你可以试试这个:
SELECT RATE_VALUE, RATE_TYPE,
CASE
WHEN
cd.PRCDR_CODE = pr.PRCDR_CODE -- optional WHERE clause 1
AND cd.MDFR_CODE = pr.MDFR_CODE -- optional WHERE clause 2
AND cd.SPLTY = pr.SPLTY -- optional WHERE clause 3
THEN 1
WHEN
cd.PRCDR_CODE = pr.PRCDR_CODE -- optional WHERE clause 1
AND cd.MDFR_CODE = pr.MDFR_CODE -- optional WHERE clause 2
THEN 2
WHEN
cd.PRCDR_CODE = pr.PRCDR_CODE -- optional WHERE clause 1
THEN 3
END matching_factor
FROM CLAIM_DOCUMENT cd, PROVIDER_RATE pr
WHERE cd.hdr_sid = p_hdr_sid -- p_hdr_sid is passed as parameter to procedure for 1 claim
AND cd.PRVDR_NPI = pr.PRVDR_NPI
ORDER BY 3;
这样您将捕获所有满足连接条件 (cd.PRVDR_NPI = pr.PRVDR_NPI
) 的记录,matching_factor
将告诉您还满足哪些附加条件 (1
=> 满足所有 3 个可选子句,3
=> 仅满足 PRCDR_CODE 子句)。
根据这个匹配因子对结果集进行排序,第一行应该是最佳匹配。
这次我做对了吗?
【讨论】:
感谢 GoranM 的支持。您的解决方案当然看起来更好。当我们必须在设计和实施过程中了解所有可能的排列时,您的解决方案会很有效。我更感兴趣的是检查是否有新的更改请求进来,其中说检查 PRVDR_NPI 和 SPLY,这应该是最高优先级。我们可以在不更改代码的情况下满足此更改请求吗?如果您需要更多详细信息,请告诉我。再次感谢 GoranM以上是关于在 SQL 中动态构建 WHERE 子句,无需硬解析即可执行的主要内容,如果未能解决你的问题,请参考以下文章
有没有比在开头使用 1=1 更好的方法来动态构建 SQL WHERE 子句?