SQL查询查找相关项

时间:2013-09-26 12:00:17

标签: c# asp.net sql sql-server

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

确定当前数据库格式的一些背景知识:

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万个数据记录,该数据库最近已被重新编入索引,这似乎确实对性能有积极影响。我们计划相对较快地对此进行维护,但我们仍将保留当前存储的大部分数据。

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

从记录数量可以看出,计时问题非常重要,因为执行当前代码需要几分钟。

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:1)

性能问题是因为您为列表中的每个字符串运行了四个查询(对于用户而言,表3和表1为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注入问题而不建议这样做,或者您可以创建一个参数化查询或存储过程,这在安全性方面要好得多。

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

答案 1 :(得分:1)

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

这将在一个记录集中返回您想要的数据,而不是之前的多个呼叫。

接下来的问题是控制数据是什么类型的数据?这就是你可能遇到问题的地方。

如果将其定义为varchar(max)或文本,则必须执行全表扫描。你不能使用常规索引&lt; 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 Server使用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