条件连接 (Oracle)
conditional join (Oracle)
我们有前端应用程序,用户可以在其中输入客户 and/or PO 来检索数据。
例如,如果用户想要检索客户的所有 PO,他将在 PO 字段中输入“%”。
如果用户想要检索所有数据,他将在每个字段中输入“%”。
我正在尝试这个,但它不起作用
SELECT *
FROM PO_INFO
WHERE customer_id = case when '%' then customer_id else 'Macys' end
AND purchase_order = case when '%' then purchase_order else '79124' end
我错过了什么?
你不应该(有些人会说 必须 不)只是将 user-entered 搜索值作为文字插入到你的 SQL 查询中。也就是说,
AND purchase_order = case when '%' then purchase_order else '79124' end
... 不会很好地执行或扩展,因为在 Oracle 看来,每个搜索都像一个全新的 SQL 查询,必须进行解析和优化(即“硬解析”)。这是一个昂贵的过程,还需要很多锁存器,这意味着多个用户试图同时 运行 搜索将不得不互相等待。
相反,您应该使用绑定变量构建 SQL。所以,
AND purchase_order = :1 -- or something. We'll discuss the exact logic later...
:1 是一个绑定变量,它是您的代码在提交查询时将提供的值的占位符。现在,每个进行搜索的人都在使用相同的查询(只是每个人都为绑定变量提供不同的值)。不再避免过多的硬解析和性能灾难。
但是,这是下一个问题。所有人的一个查询意味着它只会被硬解析一次(很好),但它也意味着每个人 运行s 使用相同的执行计划。但在这样的搜索中,一种尺寸并不适合所有人。假设 Oracle 提出的执行计划使用列 'ABC' 上的索引。如果用户没有为列 'ABC' 提供绑定变量值,该执行计划仍将遵循,但结果很糟糕。
因此,我们真正想要的是一个 SQL 用于每组有值或无值的绑定变量,而不是一个 SQL 用于每组不同的文字搜索值。
从以下字符串开始在代码中构建您的 SQL:
SELECT * FROM PO_INFO WHERE 1=1
然后,为每个搜索条件添加这个(如果值为 %)
AND (:1 IS NULL) -- and pass `NULL`, not "%" as the value for :1
(旁白:这种情况的原因,本质上是 NULL IS NULL
是为了使必须传入的绑定变量的数量和顺序始终相同,无论最终用户做什么或不给你一个价值。这使得提交某些语言的 SQL 变得容易得多,例如 PL/SQL).
如果搜索条件不是%,添加:
AND (customer_id /* or whatever column */ = :1) -- and pass the user-entered value
因此,例如,如果用户指定了 customer_id
和 po_date
的值而不是 purchase_order
,您的最终 SQL 可能如下所示:
SELECT *
FROM PO_INFO
WHERE 1=1
AND customer_id = :1
AND po_date := 2
AND :3 IS NULL -- this is purchase order and :3 you will pass as null
如果你做到了这一切,你将获得最少的 hard-parsing 和每次搜索的最佳执行计划。
我们有前端应用程序,用户可以在其中输入客户 and/or PO 来检索数据。 例如,如果用户想要检索客户的所有 PO,他将在 PO 字段中输入“%”。 如果用户想要检索所有数据,他将在每个字段中输入“%”。 我正在尝试这个,但它不起作用
SELECT *
FROM PO_INFO
WHERE customer_id = case when '%' then customer_id else 'Macys' end
AND purchase_order = case when '%' then purchase_order else '79124' end
我错过了什么?
你不应该(有些人会说 必须 不)只是将 user-entered 搜索值作为文字插入到你的 SQL 查询中。也就是说,
AND purchase_order = case when '%' then purchase_order else '79124' end
... 不会很好地执行或扩展,因为在 Oracle 看来,每个搜索都像一个全新的 SQL 查询,必须进行解析和优化(即“硬解析”)。这是一个昂贵的过程,还需要很多锁存器,这意味着多个用户试图同时 运行 搜索将不得不互相等待。
相反,您应该使用绑定变量构建 SQL。所以,
AND purchase_order = :1 -- or something. We'll discuss the exact logic later...
:1 是一个绑定变量,它是您的代码在提交查询时将提供的值的占位符。现在,每个进行搜索的人都在使用相同的查询(只是每个人都为绑定变量提供不同的值)。不再避免过多的硬解析和性能灾难。
但是,这是下一个问题。所有人的一个查询意味着它只会被硬解析一次(很好),但它也意味着每个人 运行s 使用相同的执行计划。但在这样的搜索中,一种尺寸并不适合所有人。假设 Oracle 提出的执行计划使用列 'ABC' 上的索引。如果用户没有为列 'ABC' 提供绑定变量值,该执行计划仍将遵循,但结果很糟糕。
因此,我们真正想要的是一个 SQL 用于每组有值或无值的绑定变量,而不是一个 SQL 用于每组不同的文字搜索值。
从以下字符串开始在代码中构建您的 SQL:
SELECT * FROM PO_INFO WHERE 1=1
然后,为每个搜索条件添加这个(如果值为 %)
AND (:1 IS NULL) -- and pass `NULL`, not "%" as the value for :1
(旁白:这种情况的原因,本质上是 NULL IS NULL
是为了使必须传入的绑定变量的数量和顺序始终相同,无论最终用户做什么或不给你一个价值。这使得提交某些语言的 SQL 变得容易得多,例如 PL/SQL).
如果搜索条件不是%,添加:
AND (customer_id /* or whatever column */ = :1) -- and pass the user-entered value
因此,例如,如果用户指定了 customer_id
和 po_date
的值而不是 purchase_order
,您的最终 SQL 可能如下所示:
SELECT *
FROM PO_INFO
WHERE 1=1
AND customer_id = :1
AND po_date := 2
AND :3 IS NULL -- this is purchase order and :3 you will pass as null
如果你做到了这一切,你将获得最少的 hard-parsing 和每次搜索的最佳执行计划。