假设您有一个表格如下:
Table Name: CUSTOMER Primary Key: CUSTOMER_ID +-------------+---------------+ | CUSTOMER_ID | CUSTOMER_NAME | +-------------+---------------+ | 1 | Bill | | 2 | Tom | +-------------+---------------+
现在,假设您有一个CUSTOMER_ATTRIBUTE
表,可以将键/值对绑定到特定的CUSTOMER
:
Table Name: CUSTOMER_ATTRIBUTE Primary Key: (CUSTOMER_ID, ATTRIBUTE_TYPE_ID) +-------------+-------------------+-----------------+ | CUSTOMER_ID | ATTRIBUTE_TYPE_ID | ATTRIBUTE_VALUE | +-------------+-------------------+-----------------+ | 1 | FAVORITE_FOOD | Pizza | | 1 | FAVORITE_COLOR | Blue | | 2 | FAVORITE_FOOD | Taco | | 2 | NAME_OF_PET | Fido | +-------------+-------------------+-----------------+
现在,假设您创建了一个代表具有某些可能属性的客户的视图:
CREATE VIEW CUSTOMER_VIEW AS
SELECT
CUSTOMER.CUSTOMER_ID,
CUSTOMER.CUSTOMER_NAME,
FAVORITE_FOOD_ATTRIBUTE.ATTRIBUTE_VALUE AS FAVORITE_FOOD,
FAVORITE_COLOR_ATTRIBUTE.ATTRIBUTE_VALUE AS FAVORITE_COLOR
FROM
CUSTOMER
LEFT OUTER JOIN CUSTOMER_ATTRIBUTE favorite_food_attribute
ON customer.customer_id = favorite_food_attribute.customer_id
AND favorite_food_attribute.attribute_type_id = FAVORITE_FOOD
LEFT OUTER JOIN CUSTOMER_ATTRIBUTE favorite_color_attribute
ON customer.customer_id = favorite_color_attribute.customer_id
AND favorite_color_attribute.attribute_type_id = FAVORITE_COLOR
现在,假设您查询此视图:
SELECT
CUSTOMER_ID,
CUSTOMER_NAME,
FAVORITE_COLOR
-- Notice: I did not ask for the FAVORITE_FOOD column
FROM
CUSTOMER_VIEW
根据解释计划,Oracle仍然加入favorite_food_attribute
,即使它不需要它的值,也不会影响查询的基数(因为它LEFT OUTER JOIN
到表的主键。
有没有办法强制Oracle避免这些不必要的连接?
更新:示例DDL
这是一些用于创建示例模式的DDL:
CREATE TABLE CUSTOMER
(
CUSTOMER_ID NUMBER NOT NULL,
CUSTOMER_NAME VARCHAR2(100)
);
CREATE UNIQUE INDEX CUSTOMER_PK_INDEX
ON CUSTOMER(CUSTOMER_ID);
ALTER TABLE CUSTOMER
ADD CONSTRAINT CUSTOMER_PK
PRIMARY KEY (CUSTOMER_ID)
USING INDEX CUSTOMER_PK_INDEX;
CREATE TABLE CUSTOMER_ATTRIBUTE
(
CUSTOMER_ID NUMBER NOT NULL,
ATTRIBUTE_TYPE_ID NUMBER NOT NULL,
ATTRIBUTE_VALUE VARCHAR2(1000)
);
CREATE UNIQUE INDEX CUSTOMER_ATTRIBUTE_PK_INDEX
ON CUSTOMER_ATTRIBUTE(CUSTOMER_ID, ATTRIBUTE_TYPE_ID);
ALTER TABLE CUSTOMER_ATTRIBUTE
ADD CONSTRAINT CUSTOMER_ATTRIBUTE_PK
PRIMARY KEY (CUSTOMER_ID, ATTRIBUTE_TYPE_ID)
USING INDEX CUSTOMER_ATTRIBUTE_PK_INDEX;
CREATE OR REPLACE VIEW CUSTOMER_VIEW AS
SELECT
CUSTOMER.CUSTOMER_ID,
CUSTOMER.CUSTOMER_NAME,
favorite_food_attribute.attribute_value AS favorite_food,
favorite_color_attribute.attribute_value AS favorite_color
FROM
CUSTOMER
LEFT OUTER JOIN CUSTOMER_ATTRIBUTE favorite_food_attribute
ON customer.customer_id = favorite_food_attribute.customer_id
AND favorite_food_attribute.attribute_type_id = 5
LEFT OUTER JOIN CUSTOMER_ATTRIBUTE favorite_color_attribute
ON customer.customer_id = favorite_color_attribute.customer_id
AND favorite_color_attribute.attribute_type_id = 6;
现在,我对此查询运行解释计划:
SELECT CUSTOMER_ID FROM HFSMMM.CUSTOMER_VIEW
计划是:
SELECT STATEMENT, GOAL = ALL_ROWS Cost=1 Cardinality=1 Bytes=65 NESTED LOOPS OUTER Cost=1 Cardinality=1 Bytes=65 NESTED LOOPS OUTER Cost=1 Cardinality=1 Bytes=39 INDEX FULL SCAN Object owner=HFSMMM Object name=CUSTOMER_PK_INDEX Cost=1 Cardinality=1 Bytes=13 INDEX UNIQUE SCAN Object owner=HFSMMM Object name=CUSTOMER_ATTRIBUTE_PK_INDEX Cost=0 Cardinality=1 Bytes=26 INDEX UNIQUE SCAN Object owner=HFSMMM Object name=CUSTOMER_ATTRIBUTE_PK_INDEX Cost=0 Cardinality=1 Bytes=26
答案 0 :(得分:1)
虽然这种方法只是移动处理而不是消除它,但它使SQL变得更清晰 创建用户功能
GET_TYPE(customer_id_in NUMBER, attribute_type_id IN NUMBER) RETURN VARCHAR 2
IS
/* TO DO: Assertions, error handling */
attribute_name VARCHAR2(300);
BEGIN
SELECT attribute_value
INTO attribute_name
FROM CUSTOMER_ATTRIBUTE
WHERE customer_id = customer_id_in
and attribute_type_id - attribute_type_in;
RETURN attribute_name;
END GET_TYPE;
然后你的观点是
CREATE VIEW CUSTOMER_VIEW as
SELECT
CUSTOMER.CUSTOMER_ID,
CUSTOMER.CUSTOMER_NAME,
GET_TYPE(1, CUSTOMER.CUSTOMER_ID) AS FOOD,
GET_TYPE(2, CUSTOMER.CUSTOMER_ID) AS COLOR
FROM
CUSTOMER;
和Adam指出切换上下文有开销是正确的 我每天都用它来观看。我希望数据库提前完成工作以准备视图和查询,而不是让应用程序发送必须构造和缓存的多连接查询。
答案 1 :(得分:1)
如果您确定每个客户ID和属性类型只有一个条目,则可以执行标量子查询:
SELECT
CUSTOMER.CUSTOMER_ID,
CUSTOMER.CUSTOMER_NAME,
(select ATTRIBUTE_VALUE from CUSTOMER_ATTRIBUTE where customer_id = CUSTOMER.CUSTOMER_ID
and ATTRIBUTE_TYPE_ID='F') AS FAVORITE_FOOD
FROM
CUSTOMER
答案 2 :(得分:1)
不要使用外部联接,而是为要在视图中看到的每个属性值使用子查询。这假设您的数据是结构化的,因此没有子查询可以返回多行。
CREATE VIEW CUSTOMER_VIEW AS
SELECT CUSTOMER_ID,
CUSTOMER_NAME,
(SELECT ATTRIBUTE_VALUE FROM CUSTOMER_ATTRIBUTE ca1
WHERE ca1.CUSTOMER_ID = c.CUSTOMER_ID
AND ATTRIBUTE_TYPE_ID = 'FAVFOOD') FAVORITE_FOOD,
(SELECT ATTRIBUTE_VALUE FROM CUSTOMER_ATTRIBUTE ca2
WHERE ca2.CUSTOMER_ID = c.CUSTOMER_ID
AND ATTRIBUTE_TYPE_ID = 'PETNAME') PET_NAME,
(SELECT ATTRIBUTE_VALUE FROM CUSTOMER_ATTRIBUTE ca3
WHERE ca3.CUSTOMER_ID = c.CUSTOMER_ID
AND ATTRIBUTE_TYPE_ID = 'FAVCOLOR') FAVORITE_COLOR
FROM CUSTOMER c