Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/84.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 相当于跨多个表的复合索引?_Sql_Oracle - Fatal编程技术网

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:是的,它是一个视图-比普通视图更快/更高效。谢谢你提供的信息。我看不出进行散列连接如何使选择更快。它仍然需要对每个条件的结果进行完整扫描。我假设优化器将使用这两个索引来限制哈希连接的两个输入。当然,收件人正确、日期错误的记录,反之亦然