0

電子フォームを使用するシステムがありますが、フォームの標準的な検索機能は非常に標準以下であるため、ユーザーにとってより良いアプローチを作成するように努めています。

わかりましたので、現在のデータベース形式の背景を少し説明します:

Table1
------
FormID
FormName
Creator
CreateDate

Table2
------
FormID
FormControlName
ControlData

この関係は 1 対多で、1 つのフォームに多数のコントロールがあります。

私の仕事は、フォーム名 (データに基づく変更) とそのフォームに属する各フォーム コントロールを検索して、関連するフォームを見つける検索を作成することです。

私は 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 のフォームと合計 1,040 万のデータ レコードがあり、データベースは最近再インデックス化されており、パフォーマンスにプラスの影響を与えているようです。これについては比較的近いうちにメンテナンスを行う予定ですが、現在保存されているデータの大部分は引き続き保持されます。

編集: 問題のデータベースはサードパーティ製ソフトウェアの一部であり、読み取り専用アクセスに制限されています。データベースの変更はできません。

レコードの数からわかるように、現在のコードを実行するのに文字通り数分かかるため、タイミングの問題が非常に大きいことがわかります。

どんな助けでも大歓迎ですありがとう。

4

2 に答える 2

1

TSQL では、1 対 N の関係を簡単に作成できます。以下のサンプル プログラムは、フォーム ID とコントロール ID の両方の主キーを外部キー関係で追加します。

これにより、以前に行っていた複数の呼び出しに対して、1 つのレコード セットで必要なデータが返されます。

次の質問は、制御データとはどのようなデータですか? それが問題を抱えている可能性がある場所です。

varchar(max) またはテキストとして定義されている場合は、完全なテーブル スキャンを実行する必要があります。900 バイト未満の通常のインデックスは使用できません。

インデックス検索またはバランス ツリー検索は、N 操作であるテーブル検索に対して、最悪でも N LOG(N) 操作です。これは、アルゴリズムの分析、大きさの順序、http://en.wikipedia.org/wiki/Big_O_notation、O (N LOG(N)) 対 O(N) です。

N = 11 M の場合、最大 1.04 M 行を確認するだけで済みます。これは二分木を想定しています。SQL サーバーは B+tree を使用します。http://sqlity.net/en/563/index-misconceptions-tsql-tuesday-026-second-chances/

制御データ フィールドがテキストの場合は、全文索引を適用します。私のブログ記事http://craftydba.com/?p=1421および/またはセットアップ方法に関するプレゼンテーションをご覧ください。検索には CONTAINS() または FREETEXT() 関数を使用する必要があります。

このインデックスは、データ ロードの直後に作成できますが、従来の LIKE 句よりも高速です。これにより、クライアント (Web サーバー) ではなく SQL サーバーに検索負荷 (計算) がプッシュされます。

これがお役に立てば幸いです。

さらに質問がある場合は、質問してください。

心から

ジョン

--
-- 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 に答える
1

パフォーマンスの問題は、リスト内の文字列ごとに 4 つのクエリ (Table1 に対して 3 つ、User に対して 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 コマンドを作成するか、代わりにパラメーター化されたクエリまたはセキュリティの観点からはるかに優れたストアド プロシージャを作成することができます。

上記のすべては、コードの最初の部分にのみ適用されますが、2 番目の部分については簡単に複製できます。

于 2013-09-26T12:24:51.873 に答える