最有效的设计是在我的数据库中搜索这些数据?

时间:2009-05-28 00:19:07

标签: sql-server architecture full-text-search views

我有以下数据库表和表示该数据的视图。这些表是 heirachial (如果这就是你描述它的方式): -

  编辑:我用3替换了我的3个表   FAKE表名/数据(本帖)   因为我在NDA下不发帖   关于项目的任何事情等等   是的..我真的没有救人   像这样的名字:)

FirstNames

FirstNameId INT PK NOT NULL IDENTITY
Name VARCHAR(100)

MiddleNames

MiddleNameId INT PK NOT NULL IDENTITY
Name VARCHAR(100) NOT NULL
FirstNameId INT FK NOT NULL

姓氏

SurnameId INT PK NOT NULL IDENTITY
Name VARCHAR(100) NOT NULL
FirstNameId INT FK NOT NULL

因此,firstname是父表,其他两个表是子表。

视图看起来像......

PersonNames

FirstNameId
FirstName
MiddleNameId
MiddleName
SurnameId
Surname

以下是一些示例数据。

FNID FN   MNID    MN     SNID  SN
-----------------------------------
  1  Joe    1   BlahBlah   1  Blogs
  2  Jane   -    -         1  Blogs
  3  Jon    -    -         2  Skeet

现在问题就在于此。 如何有效搜索视图中的名称?我将要有一个全文搜索/目录,但我不能把它放在一个视图上(或者至少我无法使用GUI对视图工作)。

编辑#2:以下是一些示例搜索查询: -

exec uspSearchForPeople 'joe blogs'  (1 result)
exec uspSearchForPeople 'joe'        (1 result)
exec uspSearchForPeople 'blogs'      (2 results)
exec uspSearchForPeople 'jon skeet'  (1 result)
exec uspSearchForPeople 'skeet'      (1 result)

我应该生成一个全名的新表吗?那怎么样?

请帮忙!

4 个答案:

答案 0 :(得分:0)

这似乎不是最合乎逻辑的设计决定。你为什么这样设计呢?

您目前的索引结构是什么? 3个表中每个表上的Name的索引应该加快查询速度吗?

或者,进一步规范化并创建一个Name表并在三者中分别使用NameID,然后索引Name表也应该提高性能,但我认为索引3个表上的name字段会更容易并且也可以工作。

更新与选择的统计数据是什么,因为添加这些索引可能会导致性能下降。

答案 1 :(得分:0)

疯狂的设计,可能假表名使它比它更奇怪。

根据选择用法创建索引。

如果您正在搜索像“Joe”这样的实际名字,则需要FirstNames.Name上的索引

如果你在123这样的名字ID上搜索,你有一个索引:FirstNames.FirstNameId

如果你想搜索FirstNames.name和/或MiddleNames.name和/或Surnames.name,你需要对你将要使用的组合有索引,而且你做的越多,查询就越难以选择最好的一个。

抛弃视图并为此目的编写专用查询:

追逐第一个/中间

select
  FirstNames.name
    ,MiddleNames.name
    ,Surnames.name
  FROM FirstNames
    INNER JOIN MiddleNames ON FirstNames.FirstNameId=MiddleNames.FirstNameId 
    INNER JOIN Surnames ON FirstNames.FirstNameId=Surnames.FirstNameId 
  WHERE FirstNames.Name='John'
    AND MiddleNames.Name='Q'

追问

select
  FirstNames.name
    ,MiddleNames.name
    ,Surnames.name
  FROM Surnames 
    INNER JOIN FirstNames ON Surnames.FirstNameId =FirstNames.FirstNameId
    INNER JOIN MiddleNames ON FirstNames.FirstNameId=MiddleNames.FirstNameId 
  WHERE Surnames.Name='Public'

只需确保在“where”子句

中有索引覆盖主表

使用SET SHOWPLAN_ALL ON确保使用索引(“扫描”不好“搜索”很好“)

修改
如果可能的话,在搜索之前将这些名称分开:

exec uspSearchForPeople 'joe',null,'blogs'  (1 result)
exec uspSearchForPeople 'joe',null,null     (1 result)
exec uspSearchForPeople  null,null,'blogs'  (2 results)
exec uspSearchForPeople 'jon',null,'skeet'  (1 result)
exec uspSearchForPeople null,null,'skeet'   (1 result)

在存储过程中,有三个查询:

if @GivenFirstName is not null 
    --search from FirstNames where FirstNames.name=@value & join in other tables
else if @GivenMiddleName is not null 
    --search from MiddleNames where MiddleNames.name=@value & join in other tables
else if @GivenLastName is not null 
    --search from Surnames where Surnames.name=@value & join in other tables
else --error no names given

在Names的所有三个表上都有一个索引。

如果你不能分开这些名字,我认为你运气不好,你必须对每张桌子的每一行进行扫描。

如果您不使用索引而想要一个电话簿,并且您正在寻找一个名称,那么您需要阅读整本书

答案 2 :(得分:0)

我只有一个名称类型为列的表(第一个,中间的,最后一个)和一个带有名称列上的聚集索引的FK。

   CREATE TABLE [Name] (
        NameID INT NOT NULL IDENTITY,
        [Name] varchar(100) not null,
        NameType varchar(1) not null,
        FirstNameID int null,
    )

    ALTER TABLE [Name] ADD CONSTRAINT PK_Name  PRIMARY KEY NONCLUSTERED (NameID)
    ALTER TABLE [Name] ADD CONSTRAINT FK_Name_FirstNameID FOREIGN KEY (FirstNameID) REFERENCES [Name](NameID)
    CREATE CLUSTERED INDEX IC_Name ON [Name] ([Name], NameType)

    DECLARE @fid int
    INSERT [Name] ([Name], NameType, FirstNameID) VALUES ('Joe', 'F', NULL)
    SELECT @fid = scope_identity()
    INSERT [Name] ([Name], NameType, FirstNameID) VALUES ('BlahBlah', 'M', @fid)
    INSERT [Name] ([Name], NameType, FirstNameID) VALUES ('Blogs', 'L', @fid)

    INSERT [Name] ([Name], NameType, FirstNameID) VALUES ('Jane', 'F', NULL)
    SELECT @fid = scope_identity()
    INSERT [Name] ([Name], NameType, FirstNameID) VALUES ('Blogs', 'L', @fid)

    INSERT [Name] ([Name], NameType, FirstNameID) VALUES ('Jon', 'F', NULL)
    SELECT @fid = scope_identity()
    INSERT [Name] ([Name], NameType, FirstNameID) VALUES ('Skeet', 'L', @fid)

然后,你可以在存储过程中使用sp_executsql,linq to sql,或者根据要搜索的值的数量构建一个动态但是参数化的WHERE子句(或假设最多只有3个硬编码)。在代码中甚至是丑陋的字符串操作。

答案 3 :(得分:0)

我认为你想要的是索引表。在这些表中有多少表和列,因为东西被插入到数据库中,它被索引。离。

我会推荐一张桌子作为你的名字。

NameTable
----------
Id
FirstName
MiddleName
LastName

您可以拥有任意数量的普通表格......

IndexTable
----------
Id
Text 

你可以使用文本作为主键,但我总是有一个单独的id列用于主键(只是习惯)。

IndexItemTable
----------
Id
IndexId // Has a foreign key reference to IndexTable Id
ReferenceId // The record Id of where the text occures
ReferenceTable // The table where the  text occures

然后,当你插入一个名字“吉姆巴巴罗维奇弗莱明”时,你也会扫描你的索引并发现它是空的并为吉姆,巴巴罗维奇和弗莱明创建3个新记录,这些记录都具有相同的referenceId和ReferenceTable将是“ NameTable“然后您插入另一条记录,如”Jim Bradley Fleming“,您将扫描索引表并看到您已经拥有”Jim“和”Fleming“的值,因此您只需创建IndexItem,其referenceId为2,ReferenceTable为”NameTable“

通过构建和索引,您可以通过单个文本框进行搜索,并查找数据库中包含这些值的所有记录/字段。

注意:当您将索引插入到大写或小写的索引中时,您将要更改所有内容,然后使用equals(value,OrdinalIgnoreCase)。

修改 我不能只上传图像。我必须在某个地方托管它,但它与我上面的表格图没有任何不同。 IndexTable唯一的关系是IndexItemTable。我会在代码中完成剩下的工作。离。

在名称表中插入或更新新记录期间,您必须:

  1. 扫描IndexTable并查看NameTable中的每个字段是否都存在。

  2. 如果他们不这样做,您会在索引表中添加一条新记录,其中包含未找到的文本。如果他们继续执行第3步。

  3. 使用referenceId(NameTable中记录的id)和ReferenceTable(NameTable)在IndexItemTable中添加记录,然后在IndexTable中找到文本的IndexId。

  4. 然后,当他们通过您的单个文本框进行搜索时,您将搜索索引表中的每个单词,并返回IndexTable中引用的NameTable中的名称。