我制作了一个 sn-p,显示了我将沿着哪些路线工作。良好的索引选择对于加快查询速度很重要。始终检查执行计划以调整索引。
注意事项:
脚本使用临时表,但本质上它们与常规表没有什么不同。除了#select_properties,如果您打算使用脚本中概述的工作方式,则临时表应该成为常规表。
-
使用属性选择值的 ID 存储文章属性,而不是实际选择值。当这些表被 SQL Server 缓存时,这可以节省磁盘空间和内存。 SQL Server 将尽可能多地在内存中缓存表,以更快地为 select 语句提供服务。
如果文章属性表太大,SQL Server 可能不得不做磁盘 IO 来执行 select 语句,这肯定会拖慢语句。
额外的好处是,对于查找,您正在寻找 ID(整数)而不是文本(VARCHAR's)。查找整数比查找字符串快很多。
-
为表提供合适的索引以加快查询速度。为此,最好通过检查Actual Execution Plan 来分析查询。
我在下面的 sn-p 中包含了几个这样的索引。 SQL Server会根据文章属性表中的行数和统计数据,选择最佳的索引来加快查询速度。
如果 SQL Server 认为查询缺少 SQL 语句的正确索引,则实际执行计划将指示您缺少索引。当您的查询变慢时,最好通过检查 SQL Server Management Studio 中的实际执行计划来分析这些查询。
-
sn-p 使用一个临时表来指定您要查找的属性:#select_properties。通过插入属性 ID 和属性选择值 ID 来提供该表中的条件。最终选择查询选择至少有一个属性选择值适用于每个属性的文章。
您将在要选择文章的会话中创建此临时表。然后插入搜索条件,触发 select 语句,最后删除临时表。
CREATE TABLE #articles(
article_id INT NOT NULL,
article_desc VARCHAR(128) NOT NULL,
CONSTRAINT PK_articles PRIMARY KEY CLUSTERED(article_id)
);
CREATE TABLE #properties(
property_id INT NOT NULL, -- color, size, capacity
property_desc VARCHAR(128) NOT NULL,
CONSTRAINT PK_properties PRIMARY KEY CLUSTERED(property_id)
);
CREATE TABLE #property_values(
property_id INT NOT NULL,
property_choice_id INT NOT NULL, -- eg color -> black, white, red
property_choice_val VARCHAR(128) NOT NULL,
CONSTRAINT PK_property_values PRIMARY KEY CLUSTERED(property_id,property_choice_id),
CONSTRAINT FK_values_to_properties FOREIGN KEY (property_id) REFERENCES #properties(property_id)
);
CREATE TABLE #article_properties(
article_id INT NOT NULL,
property_id INT NOT NULL,
property_choice_id INT NOT NULL
CONSTRAINT PK_article_properties PRIMARY KEY CLUSTERED(article_id,property_id,property_choice_id),
CONSTRAINT FK_ap_to_articles FOREIGN KEY (article_id) REFERENCES #articles(article_id),
CONSTRAINT FK_ap_to_property_values FOREIGN KEY (property_id,property_choice_id) REFERENCES #property_values(property_id,property_choice_id)
);
CREATE NONCLUSTERED INDEX IX_article_properties ON #article_properties(property_id,property_choice_id) INCLUDE(article_id);
INSERT INTO #properties(property_id,property_desc)VALUES
(1,'color'),(2,'capacity'),(3,'size');
INSERT INTO #property_values(property_id,property_choice_id,property_choice_val)VALUES
(1,1,'black'),(1,2,'white'),(1,3,'red'),
(2,1,'4 Gb') ,(2,2,'8 Gb') ,(2,3,'16 Gb'),
(3,1,'13"') ,(3,2,'15"') ,(3,3,'17"');
INSERT INTO #articles(article_id,article_desc)VALUES
(1,'First article'),(2,'Second article'),(3,'Third article');
-- the table you have in your question, slightly modified
INSERT INTO #article_properties(article_id,property_id,property_choice_id)VALUES
(1,1,1),(1,2,2),(1,3,2), -- article 1: color=black, capacity=8gb, size=15"
(2,1,2),(2,2,2),(2,3,1), -- article 2: color=white, capacity=8Gb, size=13"
(3,1,3), (3,3,3); -- article 3: color=red, size=17"
-- The table with the criteria you are selecting on
CREATE TABLE #select_properties(
property_id INT NOT NULL,
property_choice_id INT NOT NULL,
CONSTRAINT PK_select_properties PRIMARY KEY CLUSTERED(property_id,property_choice_id)
);
INSERT INTO #select_properties(property_id,property_choice_id)VALUES
(2,1),(2,2),(3,1); -- looking for '4Gb' or '8Gb', and size 13"
;WITH aid AS (
SELECT ap.article_id
FROM #select_properties AS sp
INNER JOIN #article_properties AS ap ON
ap.property_id=sp.property_id AND
ap.property_choice_id=sp.property_choice_id
GROUP BY ap.article_id
HAVING COUNT(DISTINCT ap.property_id)=(SELECT COUNT(DISTINCT property_id) FROM #select_properties)
-- criteria met when article has a number of properties matching, equal to the distinct number of properties in the selection set
)
SELECT a.article_id,a.article_desc
FROM aid
INNER JOIN #articles AS a ON
a.article_id=aid.article_id
ORDER BY a.article_id;
-- result is the 'Second article' with id 2
DROP TABLE #select_properties;
DROP TABLE #article_properties;
DROP TABLE #property_values;
DROP TABLE #properties;
DROP TABLE #articles;