我有两个表:DOCUMENT和METADATA。 DOCUMENT存储ID和我们不感兴趣的一些信息,METADATA存储"标签"对于那些文件。标签由键和值组成。
因此,对于一个文档,DOCUMENT表中只有一个条目,但METADATA表中可能有很多条。
现在我需要的是传递一组键/值,并从METADATA表中仅检索与所有键/值匹配的文档。这意味着同时检查不同的行",好吧,我真的不知道该怎么做。
快速举例:
META_KEY | META_VALUE | META_DOCUMENT_ID
----------------------------------------
Firstname| Chris | 1
Lastname | Doe | 1
Firstname| Chris | 2
Lastname | Moe | 2
因此,如果我使用以下标签进行查询:"名字" ="克里斯","姓氏" =" Doe",我想要结果是1。如果我只指定"名字" ="克里斯"我想要1和2作为结果。
非常感谢您的帮助!
编辑:
如何计算必须匹配的标签数量? 像这样:
从元数据中选择meta_document_id,count(*),其中(meta_key ='名字'和meta_value ='克里斯')或(meta_key ='姓氏'和meta_value =' Doe')按meta_document_id分组
使用count(*),我可以很容易地找出所有输入键/值对是否匹配。如何运行性能?
答案 0 :(得分:3)
嗯,您正在使用名为“key-value”或“Entity-attributte-value”的数据库模型。
这通常不是最佳选择,您可以在以下问题中详细了解:
对于这两种情况,您需要两个单独的查询:
SELECT distinct META_DOCUMENT_ID
FROM METADATA
WHERE meta_key = 'Firstname' and meta_value = 'Chris'
SELECT distinct m1.META_DOCUMENT_ID
FROM METADATA m1
JOIN METADATA m2
ON m1.META_DOCUMENT_ID = m2.META_DOCUMENT_ID
WHERE m1.meta_key = 'Firstname' and m1.meta_value = 'Chris'
AND m2.meta_key = 'Lastname' and m2.meta_value = 'Doe'
修改强>
我想我必须在表中加入N次键/值对吗?
这可以在没有连接的情况下完成,例如下面(假设每个id的meta_key值不超过1):
SELECT META_DOCUMENT_ID
FROM METADATA
WHERE (meta_key, meta_value) IN
( ('Firstname' ,'Chris'), ('Lastname', 'Doe' ) )
GROUP BY META_DOCUMENT_ID
HAVING COUNT(*) = 2 /* 2 means that we are looking for 2 meta keys */
这将如何以性能方式运行?
苦头。请参阅上面关于此模型的链接的解释。
在许多情况下,此查询必须执行全表扫描(特别是当我们要查找的许多属性/键多于几个时),计算每个id的值,然后选择具有count = 2的这些id。
在规范化模型中,这是一个简单的查询,可以使用索引快速选择firstname ='Chris'的这几行
SELECT *
FROM table
WHERE firstname = 'Chris' and lastname = 'Doe'
答案 1 :(得分:2)
Oracle安装程序:
CREATE TYPE KEY_VALUE_PAIR IS OBJECT (
KEY VARCHAR2(50),
VALUE VARCHAR2(50)
);
/
CREATE TYPE KEY_VALUE_TABLE IS TABLE OF KEY_VALUE_PAIR;
/
CREATE TABLE meta_data ( meta_key, meta_value, meta_document_id ) AS
SELECT 'Firstname', 'Chris', 1 FROM DUAL UNION ALL
SELECT 'Lastname', 'Doe', 1 FROM DUAL UNION ALL
SELECT 'Phonenumber', '555-2368', 1 FROM DUAL UNION ALL
SELECT 'Firstname', 'Chris', 2 FROM DUAL UNION ALL
SELECT 'Lastname', 'Moe', 2 FROM DUAL UNION ALL
SELECT 'Phonenumber', '555-0001', 2 FROM DUAL;
<强>查询强>:
SELECT meta_document_id
FROM (
SELECT meta_document_id,
CAST(
COLLECT(
KEY_VALUE_PAIR( meta_key, meta_value )
) AS KEY_VALUE_TABLE
) AS key_values
FROM meta_data
GROUP BY meta_document_id
)
WHERE KEY_VALUE_TABLE(
-- Your values here:
KEY_VALUE_PAIR( 'Firstname', 'Chris' ),
KEY_VALUE_PAIR( 'Lastname', 'Doe' )
)
SUBMULTISET OF key_values;
<强>输出强>:
META_DOCUMENT_ID
------------------
1
更新 - 使用嵌套表格重新实现元数据表:
Oracle安装程序:
CREATE TYPE KEY_VALUE_PAIR IS OBJECT (
META_KEY VARCHAR2(50),
META_VALUE VARCHAR2(50)
);
/
CREATE TYPE KEY_VALUE_TABLE IS TABLE OF KEY_VALUE_PAIR;
/
CREATE TABLE meta_data (
meta_document_id INT,
key_values KEY_VALUE_TABLE
) NESTED TABLE key_values STORE AS meta_data_key_values;
CREATE UNIQUE INDEX META_DATA_KEY_VALUES_IDX ON META_DATA_KEY_VALUES (
NESTED_TABLE_ID,
META_KEY,
META_VALUE
);
/
-- Insert everything in one go:
INSERT INTO META_DATA VALUES(
1,
KEY_VALUE_TABLE(
KEY_VALUE_PAIR( 'Firstname', 'Chris' ),
KEY_VALUE_PAIR( 'Lastname', 'Doe' ),
KEY_VALUE_PAIR( 'Phonenumber', '555-2368' )
)
);
-- Insert everything in bits:
INSERT INTO meta_data VALUE ( 2, KEY_VALUE_TABLE() );
INSERT INTO TABLE( SELECT key_values FROM meta_data WHERE meta_document_id = 2 )
( meta_key, meta_value ) VALUES( 'Firstname', 'Chris' );
INSERT INTO TABLE( SELECT key_values FROM meta_data WHERE meta_document_id = 2 )
( meta_key, meta_value ) VALUES( 'Lastname', 'Moe' );
INSERT INTO TABLE( SELECT key_values FROM meta_data WHERE meta_document_id = 2 )
( meta_key, meta_value ) VALUES( 'Phonenumber', '555-0001' );
--Select all the key-value pairs:
SELECT META_DOCUMENT_ID,
META_KEY,
META_VALUE
FROM META_DATA md,
TABLE( md.KEY_VALUES );
<强>查询强>:
上述更改可让您简化查询:
SELECT META_DOCUMENT_ID
FROM meta_data
WHERE KEY_VALUE_TABLE(
-- Your values here:
KEY_VALUE_PAIR( 'Firstname', 'Chris' ),
KEY_VALUE_PAIR( 'Lastname', 'Doe' )
)
SUBMULTISET OF key_values;
答案 2 :(得分:0)
如果您事先知道所有可能的TAGS,那么可能会有一些方法PIVOT
:
with METADATA (META_KEY, META_VALUE, META_DOCUMENT_ID) as
(
select 'Firstname', 'Chris',1 from dual union all
select 'Lastname', 'Doe',1 from dual union all
select 'Firstname', 'Chris',2 from dual union all
select 'Lastname', 'Moe',2 from dual
)
select *
from metadata
PIVOT ( max (META_VALUE ) FOR (META_KEY) IN ('Firstname' AS Firstname, 'Lastname' AS Lastname))
where Firstname = 'Chris' /* and Lastname ='Doe' ...*/