0

以下のような表があります。タイプが固定および普通預金の口座があります。ユーザー1のすべてのアカウントのステータスを更新する必要があります。このユーザーには10000のアカウントがあります。基本的に、ロジックは次のSQLストアドプロシージャスクリプトに示すようになります。スクリプトの実行には1秒未満(83ミリ秒)しかかかりません。

しかし、LINQ to SQLを使用してORMに変換すると、 3分(204814ミリ秒)以上かかります。少なくとも240,000%遅くなります。

このパフォーマンスの低下を克服するのに役立つLINQtoSQL(または他のORM)のパターンはありますか?

何がデータベースに一度に更新を強制することができますか?

注:LINQからストアドプロシージャを呼び出すことを認識しています。私はそれをORMとは見なしておらず、私にとっての選択肢ではありません。

ここに画像の説明を入力してください

手動ストアドプロシージャスクリプト

DECLARE @UserID INT 
DECLARE @StatusForFixed VARCHAR(50)
DECLARE @StatusForSavings VARCHAR(50)

SET @UserID = 1
SET @StatusForFixed = 'FrozenFA11'
SET @StatusForSavings = 'FrozenSB22'

UPDATE BankAccount 
SET Status = 
        CASE 
            WHEN BankAccount.AccountType='Fixed' THEN @StatusForFixed
            WHEN BankAccount.AccountType='Savings' THEN @StatusForSavings
        END
 WHERE  AccountOwnerID=@UserID

LINQで生成されたコードサンプル

Note: This type of statements happen 10000 times

UPDATE [dbo].[BankAccount]
SET [Status] = @p3
WHERE [BankAccountID] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [3585]
-- @p3: Input NChar (Size = 10; Prec = 0; Scale = 0) [FrozenSB]

ORMを適用した後のコード

public class BankAccountAppService
{
    public RepositoryLayer.ILijosBankRepository AccountRepository { get; set; }

    public void FreezeAllAccountsForUser(int userId)
    {
        IEnumerable<DBML_Project.BankAccount> accounts = AccountRepository.GetAllAccountsForUser(userId);
        foreach (DBML_Project.BankAccount acc in accounts)
        {

            acc.Freeze();

        }
        AccountRepository.UpdateAccount();

    }

}

public class LijosSimpleBankRepository : ILijosBankRepository
{
    public System.Data.Linq.DataContext Context
    {
        get;
        set;
    }


    public List<DBML_Project.BankAccount> GetAllAccountsForUser(int userID)
    {
        IQueryable<DBML_Project.BankAccount> queryResultEntities = Context.GetTable<DBML_Project.BankAccount>().Where(p => p.AccountOwnerID == userID);
        return queryResultEntities.ToList();
    }

    public List<T> GetAllAccountsofType<T>() where T : DBML_Project.BankAccount
    {
        var query = from p in Context.GetTable<DBML_Project.BankAccount>().OfType<T>()
                    select p;

        List<T> typeList = query.ToList();
        return typeList;

    }

    public virtual void UpdateAccount()
    {
        Context.SubmitChanges();
    }

}

namespace DBML_Project
{

public  partial class BankAccount
{
    //Define the domain behaviors
    public virtual void Freeze()
    {
        //Do nothing
    }
}

public class FixedBankAccount : BankAccount
{

    public override void Freeze()
    {
        this.Status = "FrozenFA";
    }
}

public class SavingsBankAccount : BankAccount
{

    public override void Freeze()
    {
        this.Status = "FrozenSB";
    }
}  
}

参照

  1. XMLデータ型パラメーターとして使用されるXElementとしてリストを渡します
4

4 に答える 4

6

2つの大きく異なるシナリオを比較しています。

1:SQLサーバー上でローカルにスクリプトを実行する、単一のセットベースUPDATE

2:ネットワーク経由で10,000レコードをフェッチし、それぞれを更新し、それぞれを個別に送信します

1の10,000バッチではなく10,000の1つのバッチに延期することで2を少し改善できます(ただし、最後まで呼び出さないでください)が、それでも10,000レコードの詳細を2つの方向に送信する必要があります。オーバーヘッド(たとえば、10,000回の個別の呼び出しを介してそれを行うことを選択する場合があります)SubmitChanges()SubmitChanges()SubmitChanges()

基本的に、オブジェクトベースのツールは、レコードに対する一括更新を目的としていません。SPが機能する場合は、SPを使用します。メソッド/パラメータなどを追加するのに便利なように、データコンテキストを介してSPを呼び出すこともできます。

于 2012-07-03T07:38:46.593 に答える
1

アプリケーションからストアドプロシージャ/カスタムSQLスクリプトを実行することもできます。Linq-to-sqlモデルでプロシージャをマップすることもできるため、接続を開いてコマンドを手動で作成する必要はありません。

Linq-to-sqlが常にデータベースへの個別のラウンドトリップで各変更コマンドを実行するかどうかは正確にはわかりませんが、(少なくともほとんどの場合)実行されると思います。EFは常にそれを行います。NHibernateはコマンドバッチ処理を備えているため、このような操作をより適切にサポートします。

ここで示したのは、バッチ更新(多数のレコードを更新する単一のコマンド)ではありません。ほとんどのORMは、常に各レコードを個別に更新します。これが、これらのツールの動作方法です。レコードをロードし、ループ内で各レコードを変更すると、レコードのロードに使用された元のクエリとの関係が失われます。これで、アプリケーションに10.000のロードされたレコードがあり、更新する必要があります。10.000の変更をアプリケーションからデータベースに移動する必要があるため、一括更新はできません。

一括更新を行う場合は、直接SQLを使用するか、レコードを読み込んでアプリケーションで更新する代わりに、Linqからsqlに更新するロジックを実装する必要があります。この記事を確認するか、Linq-to-sqlで一括/バッチ更新を検索してください。

于 2012-07-03T08:00:10.593 に答える
0

これは、Linq to SQLが最初にサーバーからデータをロードしてから、クライアントへのデータクエリ/転送、各レコードの更新要求を含む各レコードを個別に更新するためです。一方、SPの場合、サーバー上で更新クエリを直接実行するSPの呼び出しがあり、各レコードのデータフェッチと更新は含まれません。レコードを一括更新します

于 2012-07-03T07:34:43.337 に答える
0

私が行ったもう1つのアプローチは、オブジェクト値をXMLデータ型としてストアドプロシージャに渡すことです。ただし、レコード数が1000を超えると、タイムアウト例外(約25秒後)が発生します。これは、巨大なxmlファイルが原因ですか?

注:1000レコードの場合は約5秒かかります

   public virtual void UpdateBankAccountUsingParseXML_SP(System.Xml.Linq.XElement inputXML)
    {
        string connectionstring = "Data Source=.;Initial Catalog=LibraryReservationSystem;Integrated Security=True;Connect Timeout=600";
        var myDataContext = new DBML_Project.MyDataClassesDataContext(connectionstring);
        myDataContext.ParseXML(inputXML);

    }

    public void FreezeAllAccountsForUser(int userId)
    {
        List<DTOLayer.BankAccountDTOForStatus> bankAccountDTOList = new List<DTOLayer.BankAccountDTOForStatus>(); 

        IEnumerable<DBML_Project.BankAccount> accounts = AccountRepository.GetAllAccountsForUser(userId);
        foreach (DBML_Project.BankAccount acc in accounts)
        {
            string typeResult = Convert.ToString(acc.GetType());
            string baseValue = Convert.ToString(typeof(DBML_Project.BankAccount));

            if (String.Equals(typeResult, baseValue))
            {
                throw new Exception("Not correct derived type");
            }

            acc.Freeze();

            DTOLayer.BankAccountDTOForStatus presentAccount = new DTOLayer.BankAccountDTOForStatus();
            presentAccount.BankAccountID = acc.BankAccountID;
            presentAccount.Status = acc.Status;
            bankAccountDTOList.Add(presentAccount);

        }



        IEnumerable<System.Xml.Linq.XElement> el = bankAccountDTOList.Select(x =>
                        new System.Xml.Linq.XElement("BankAccountDTOForStatus",
                          new System.Xml.Linq.XElement("BankAccountID", x.BankAccountID),
                          new System.Xml.Linq.XElement("Status", x.Status)
                        ));

        System.Xml.Linq.XElement root = new System.Xml.Linq.XElement("root", el);


        AccountRepository.UpdateBankAccountUsingParseXML_SP(root);
        //AccountRepository.Update();

    }

ストアドプロシージャ

ALTER PROCEDURE [dbo].[ParseXML] (@InputXML xml)
AS
BEGIN

DECLARE @MyTable TABLE (RowNumber int, BankAccountID int, StatusVal varchar(max))

INSERT INTO @MyTable(RowNumber, BankAccountID,StatusVal)

SELECT ROW_NUMBER() OVER(ORDER BY c.value('BankAccountID[1]','int') ASC) AS Row,
    c.value('BankAccountID[1]','int'),
    c.value('Status[1]','varchar(32)')
FROM
    @inputXML.nodes('//BankAccountDTOForStatus') T(c);


DECLARE @Count INT
SET @Count = 0

DECLARE @NumberOfRows INT
SELECT @NumberOfRows = COUNT(*) FROM @MyTable


WHILE @Count < @NumberOfRows
BEGIN



      SET @Count = @Count + 1

     DECLARE @BankAccID INT
     DECLARE @Status VARCHAR(MAX)

     SELECT @BankAccID = BankAccountID 
     FROM @MyTable
     WHERE RowNumber = @Count

     SELECT @Status = StatusVal 
     FROM @MyTable
     WHERE RowNumber = @Count


     UPDATE BankAccount 
     SET Status= @Status
     WHERE BankAccountID = @BankAccID

END


END


GO
于 2012-07-04T14:52:14.337 に答える