0

我们有一个使用电子表格的系统,但表格的标准搜索功能非常低,所以我的任务是尝试为用户创建更好的方法。

好的,当前数据库格式的一些背景:

Table1
------
FormID
FormName
Creator
CreateDate

Table2
------
FormID
FormControlName
ControlData

这种关系是一对多的关系,一个表单上有很多控件。

我的任务是创建一个搜索,通过搜索表单名称(基于数据的更改)和属于该表单的每个表单控件来查找相关表单。

我已经设法使用 C# 编码来做到这一点,但是因为数据库中有大量记录,而且在我当前的解决方案中我正在检索所有内容并迭代它以找到相关项目,这是一个相当缓慢的解决方案.

我的代码:

private DataTable GetForms() {
    string FormName = ddForm.SelectedValue;
    string SearchText = tbSearch.Text;

    List<string> FormIDs = GetMatchingForms(FormName, SearchText);

    DataTable dtForms = new DataTable("Forms Table");

    dtForms.Columns.Add("Form Name");
    dtForms.Columns.Add("Initiator");
    dtForms.Columns.Add("Start Date").DataType = typeof(DateTime);
    dtForms.Columns.Add("FormLink");

    foreach (string FormID in FormIDs) {
        DataRow nRow = dtForms.NewRow();

        nRow[0] = GetData.GetString("SELECT [FormName] FROM [Table1] Where [FormID] = '" + FormID + "'", conString);

        string UserID = GetData.GetString("SELECT [Creator] FROM [Table1] Where [FormID] = '" + FormID + "'", conString);
        string UserName = GetData.GetString("Select [UserName] From [User] Where [UserID] = '" + UserID + "'", conString);
        nRow[1] = UserName;

        nRow[2] = GetData.GetString("SELECT [CreateDate] FROM [Table1] Where [FormID] = '" + FormID + "'", conString);
        nRow[3] = "~/Form.aspx?formid=" + FormID;

        dtForms.Rows.Add(nRow);
    }

    return dtForms;
}
private List<string> GetMatchingForms(string FormName, string SearchText) {
    //FormName can be = % to search all forms
    DataTable dtForms = GetData.GetDT("SELECT * FROM [Table1] Where [FormName] LIKE '%" + FormName + "%'", conString);

    List<string> FormList = new List<string>();

    foreach (DataRow row in dtForms.Rows) {
        string FormName = row["FormName"].ToString();
        string FormID = row["FormID"].ToString();

        bool Relevant = false;

        if (FormName.Contains(SearchText)) {
            Relevant = true;
        } else {
            DataTable dtFormControls = GetData.GetDT("SELECT * FROM [Table2] Where [FormID] = '" + FormID + "'", conString);

            foreach (DataRow cRow in dtFormControls.Rows) {
                string ControlData = cRow["ControlData"].ToString();

                if (ControlData.Contains(SearchText)) {
                    Relevant = true;
                    break;
                }
            }
        }

        if (Relevant) {
            FormList.Add(FormID);
        }
    }

    return FormList;
}

我想知道是否可以将上述代码的功能复制到 SQL 查询(或少量查询)中,以希望尝试加速当前的解决方案。我目前对 SQL 查询的了解并不是最好的,我什至想不出从哪里开始考虑这个问题。

澄清一下,我们目前有 300,000 个表格和总共 1040 万条数据记录,该数据库最近已重新索引,这似乎确实对性能产生了积极影响。我们计划相对较快地对此进行维护,但我们仍将保留当前存储的大部分数据。

编辑:有问题的数据库是第 3 方软件的一部分,我们仅限于只读访问,我们无法修改数据库。

从记录的数量可以看出,时间问题是一个很大的问题,因为执行当前代码实际上需要几分钟。

任何帮助将不胜感激,谢谢。

4

2 回答 2

1

性能问题是因为您正在为列表中的每个字符串运行四个查询(3 个针对 Table1 和 1 个针对用户),这是一个很大的开销。我推荐以下之一(请记住我无权访问您的数据库,对于任何编码错误,我们深表歉意)

1) 使用 LinqToSql

如果您使用 LinqToSql,那么您可以使用类似于以下内容的查询的第一部分中提取数据:

var myResults = (from t in context.Table1
                 join u in context.User on t.UserId equals u.UserId
                 where formIds.Contains (t.FormId)
                 select new { t.FormName, t.Creator, t.CreateDate }).ToList();

Contains 方法允许您有效地将内存中的数据与数据库中的数据连接起来,从而无需为每个项目循环。

2)使用数据库查询

等效的 SQL 语句将是:

select t.FormName, t.Creator, t.CreateDate
from Table1 t
inner join [User] u on t.UserID = u.UserId
where t.FormId in (<list of formIDs here>)

您可以创建一个 SQL 命令,构建由于 SQL 注入问题而不推荐的字符串,或者您可以创建参数化查询或存储过程,这从安全角度来看要好得多。

以上所有内容仅适用于代码的第一部分,但可以很容易地复制到第二部分。

于 2013-09-26T12:24:51.873 回答
1

可以在 TSQL 中轻松创建 1 对 N 的关系。下面的示例程序为具有外键关系的表单 id 和控件 id 添加主键。

这将在一个记录集中返回您想要的数据,而不是您之前进行的多次调用。

下一个问题是什么类型的数据是控制数据?那就是您可能遇到问题的地方。

如果它被定义为 varchar(max) 或 text,那么您必须执行全表扫描。您不能使用 < 900 字节的常规索引。

索引或平衡树搜索在最坏的情况下是 N LOG(N) 操作,而表搜索是 N 操作。这是对算法的分析,数量级,http ://en.wikipedia.org/wiki/Big_O_notation,O (N LOG(N)) 与 O(N)。

在 N = 11 M 时,您最多只需要查看 1.04 M 行。这是假设二叉树。SQL 服务器使用 B+树。http://sqlity.net/en/563/index-misconceptions-tsql-tuesday-026-second-chances/

如果控制数据字段是文本,则要应用全文索引。查看我的博客文章http://craftydba.com/?p=1421和/或有关如何设置的演示文稿。您必须使用 CONTAINS() 或 FREETEXT() 函数进行搜索。

此索引可以在数据加载后立即构建,但与传统的 LIKE 子句相比,速度更快。这会将搜索负载(计算)推送到 SQL 服务器而不是客户端(Web 服务器)。

我希望这可以帮助你,

如果您还有其他问题,请尽管提问。

真挚地

约翰

--
-- Sample table 1
--

create table tempdb.dbo.forms
(
    FormID int identity(1,1) primary key clustered,
    FormName varchar(32),
    Creator varchar(32) DEFAULT (coalesce(suser_sname(),'?')),
    CreateDate smalldatetime DEFAULT (getdate()) 
);
go

-- Add data
insert into tempdb.dbo.forms (FormName) values ('Main');
go

-- Show the data
select * from tempdb.dbo.forms;
go


--
-- Sample table 2
--

create table tempdb.dbo.controls
(
ControlId int identity(1,1) primary key clustered,
FormID int,
FormControlName varchar(32),
ControlData varchar(32)
);
go

-- Add foreign key 2 forms table
ALTER TABLE tempdb.dbo.controls WITH CHECK 
ADD CONSTRAINT fk_tbl_forms FOREIGN KEY(FormId)
REFERENCES tempdb.dbo.forms (FormID)
go

-- Add data
insert into tempdb.dbo.controls (FormId, FormControlName, ControlData)
values 
(1, 'Drop Down', 'My drop down data'),
(1, 'Text Box', 'My text box');
go

-- Show the data
select * from tempdb.dbo.controls;
go


--
-- Use a join command (1 x N) relationship with where
--

-- Show data from both
select 
    f.FormID,
    f.FormName,
    f.Creator,
    f.CreateDate,
    c.ControlId,
    c.FormControlName,
    c.ControlData 
from 
    tempdb.dbo.forms as f inner join 
    tempdb.dbo.controls as c
on 
    f.FormID = c.FormID 
where 
    f.FormName like '%Main%' and
    c.ControlData like '%box%'
go
于 2013-09-26T12:54:00.907 回答