Sql 相当于跨多个表的复合索引?
我有一个类似于以下的表结构:Sql 相当于跨多个表的复合索引?,sql,oracle,Sql,Oracle,我有一个类似于以下的表结构: create table MAIL ( ID int, FROM varchar, SENT_DATE date ); create table MAIL_TO ( ID int, MAIL_ID int, NAME varchar ); 我需要运行以下查询: select m.ID from MAIL m inner join MAIL_TO t on t.MAIL_ID = m.ID
create table MAIL (
ID int,
FROM varchar,
SENT_DATE date
);
create table MAIL_TO (
ID int,
MAIL_ID int,
NAME varchar
);
我需要运行以下查询:
select m.ID
from MAIL m
inner join MAIL_TO t on t.MAIL_ID = m.ID
where m.SENT_DATE between '07/01/2010' and '07/30/2010'
and t.NAME = 'someone@example.com'
有没有办法设计索引,使两个条件都可以使用索引?如果我在MAIL.SENT_DATE上放置索引,在MAIL_TO.NAME上放置索引,则数据库将选择使用其中一个索引或另一个索引,而不是同时使用这两个索引。按第一个条件筛选后,数据库始终必须对第二个条件的结果进行完整扫描。a允许您对值进行索引,前提是满足严格的物化视图条件。哪个条件更具选择性?日期范围还是收件人?我猜是收件人。如果这是高度选择性的,则不必考虑日期索引,只需让数据库根据找到的邮件ID进行搜索即可。但是索引表
MAIL
(如果还没有)
另一方面,一些现代优化器甚至会使用这两个索引,扫描这两个表,然后构建连接列的哈希值来合并这两个表的结果。我不能绝对确定甲骨文是否以及何时会选择这一战略。我刚刚意识到,与其他引擎相比,SQL Server更倾向于进行哈希连接。Oracle可以同时使用这两个索引。你只是没有正确的两个指数 考虑:如果查询计划首先在
mail.sent\u date
上使用您的索引,那么它从mail
中得到了什么?它获取所有mail.id
s,其中mail.sent\u日期
在您的where
子句中给出的范围内,是吗
因此,它转到mail\u to
,其中包含mail.id
s列表和您在where
子句中给出的mail.name
。此时,Oracle决定最好扫描表以匹配mail\u to.mail\u id
s,而不是使用mail\u to.name
上的索引
varchar上的索引总是有问题的,Oracle确实更喜欢全表扫描。但是,如果我们给Oracle一个包含它真正想要使用的列的索引,并根据表行总数和统计数据,我们可以让它使用它。这是索引:
create index mail_to_pid_name on mail_to( mail_id, name ) ;
这在name
上的索引不起作用的情况下起作用,因为Oracle不只是查找名称,而是查找mail\u id
和name
相反,如果基于成本的分析器确定首先转到表mail\u to
更便宜,并在mail\u to.name
上使用您的索引,您会得到什么?要在mail
中查找的一组mail\u to.\u.mail\u id
s。它需要查找具有这些ID和特定发送日期的行,因此:
create index mail_id_sentdate on mail( sent_date, id ) ;
注意,在本例中,我将sent\u date
放在索引的第一位,将id
放在第二位。(这是一件更直观的事情。)
再一次,回溯点是:在创建索引时,不仅要考虑<代码>中的列所在的子句,还要考虑联接条件中的列。
更新
jthg:是的,这总是取决于数据的分布方式。表中有多少行:如果行数很多,Oracle将进行表扫描和哈希连接,如果行数很少,Oracle将进行表扫描。您可以颠倒两个索引中的任何一个的顺序。通过将sent_date放在第二个索引的第一位,我们消除了仅在
sent_date
上索引的大多数需求。如果您的查询通常是针对特定月份的,那么您可以按月查询数据。在无法满足物化视图要求的情况下,有以下两个选项:
1) 您可以创建一个交叉引用表,并使用触发器保持更新
这些概念与Oracle相同,但我目前只安装了SQL Server来运行测试,请参见以下设置:
create table MAIL (
ID INT IDENTITY(1,1),
[FROM] VARCHAR(200),
SENT_DATE DATE,
CONSTRAINT PK_MAIL PRIMARY KEY (ID)
);
create table MAIL_TO (
ID INT IDENTITY(1,1),
MAIL_ID INT,
[NAME] VARCHAR (200),
CONSTRAINT PK_MAIL_TO PRIMARY KEY (ID)
);
ALTER TABLE [dbo].[MAIL_TO] WITH CHECK ADD CONSTRAINT [FK_MAILTO_MAIL] FOREIGN KEY([MAIL_ID])
REFERENCES [dbo].[MAIL] ([ID])
GO
ALTER TABLE [dbo].[MAIL_TO] CHECK CONSTRAINT [FK_MAILTO_MAIL]
GO
CREATE TABLE CompositeIndex_MailSentDate_MailToName (
[MAIL_ID] INT,
[MAILTO_ID] INT,
SENT_DATE DATE,
MAILTO_NAME VARCHAR(200),
CONSTRAINT PK_CompositeIndex_MailSentDate_MailToName PRIMARY KEY (MAILTO_ID,MAIL_ID)
)
GO
CREATE NONCLUSTERED INDEX IX_MailSent_MailTo ON dbo.CompositeIndex_MailSentDate_MailToName (SENT_DATE,MAILTO_NAME)
CREATE NONCLUSTERED INDEX IX_MailTo_MailSent ON dbo.CompositeIndex_MailSentDate_MailToName (MAILTO_NAME,SENT_DATE)
GO
CREATE TRIGGER dbo.trg_MAILTO_Insert
ON dbo.MAIL_TO
AFTER INSERT AS
BEGIN
INSERT INTO dbo.CompositeIndex_MailSentDate_MailToName ( MAIL_ID, MAILTO_ID, SENT_DATE, MAILTO_NAME )
SELECT mailTo.MAIL_ID,mailTo.ID,m.SENT_DATE,mailTo.NAME
FROM
inserted mailTo
INNER JOIN dbo.MAIL m ON m.ID = mailTo.MAIL_ID
END
GO
CREATE TRIGGER dbo.trg_MAILTO_Delete
ON dbo.MAIL_TO
AFTER DELETE AS
BEGIN
DELETE mailToDelete
FROM
dbo.MAIL_TO mailToDelete
INNER JOIN deleted ON mailToDelete.ID = deleted.ID
END
GO
CREATE TRIGGER dbo.trg_MAILTO_Update
ON dbo.MAIL_TO
AFTER UPDATE AS
BEGIN
UPDATE compositeIndex
SET
compositeIndex.MAILTO_NAME = updates.NAME
FROM
dbo.CompositeIndex_MailSentDate_MailToName compositeIndex
INNER JOIN inserted updates ON updates.ID = compositeIndex.MAILTO_ID
END
GO
CREATE TRIGGER dbo.trg_MAIL_Update
ON dbo.MAIL
AFTER UPDATE AS
BEGIN
UPDATE compositeIndex
SET
compositeIndex.SENT_DATE = updates.SENT_DATE
FROM
dbo.CompositeIndex_MailSentDate_MailToName compositeIndex
INNER JOIN inserted updates ON updates.ID = compositeIndex.MAIL_ID
END
GO
INSERT INTO dbo.MAIL ( [FROM], SENT_DATE )
SELECT 'SenderA','2018-10-01'
UNION ALL SELECT 'SenderA','2018-10-02'
INSERT INTO dbo.MAIL_TO ( MAIL_ID, NAME )
SELECT 1,'CustomerA'
UNION ALL SELECT 1,'CustomerB'
UNION ALL SELECT 2,'CustomerC'
UNION ALL SELECT 2,'CustomerD'
UNION ALL SELECT 2,'CustomerE'
SELECT * FROM dbo.MAIL
SELECT * FROM dbo.MAIL_TO
SELECT * FROM dbo.CompositeIndex_MailSentDate_MailToName
然后,您可以使用dbo.CompositeIndex\u MailSentDate\u MailToName
表连接到其余数据。这在插入和更新率较低但查询需求较高的环境中非常有用。因此,实现触发器的相对开销很小
这具有事务性实时更新的优点
2) 如果不需要触发器的性能/管理开销,并且只需要在第二天报告时使用,则可以创建一个视图和一个夜间进程,该进程截断表并将整个视图选择到一个物化表中
我已经成功地使用它来索引平坦的关系数据,这些数据需要跨十几个表进行连接。。将报告时间从数小时减少到数秒。虽然这是一个昂贵的查询,但如果使用时间减少,您可以将作业设置为运行小时数。有什么原因您不能对这两列执行#诱惑和索引吗?@JNK:我看到了您的索引临时表,并为您提供了一个物化视图…@OMG Ponies-+1,你是专家,我只是在这里闲逛,不时插话:)而且我对Oracle也不是非常熟悉,主要使用SQL Server 2008。@JNK:我不是专家-我在等待APC、Gary、Kemp先生、Tony Andrew、Rob。。。还有一些我忘了了解甲骨文的细节,没有特别的原因。这是一个现有的数据库/应用程序,运行有点慢。示例查询代表运行的大多数查询。我希望能够通过调整索引来加快速度。我认为应用程序需要修改以引用物化视图?@jthg:是的,它是一个视图-比普通视图更快/更高效。谢谢你提供的信息。我看不出进行散列连接如何使选择更快。它仍然需要对每个条件的结果进行完整扫描。我假设优化器将使用这两个索引来限制哈希连接的两个输入。当然,收件人正确、日期错误的记录,反之亦然