1

さて、私の SQL Server 2005 データベースには次の手順があります。なぜ結果が得られないのかを理解しようとしています。アクセスしようとしているユーザーは管理者ですが、別のユーザーでアクセスしようとしましたが、まだ結果が得られません。Bond は、Bond テーブルの基本クエリで返されます。これが結果を返さない理由を理解しようとしています。どんな助けでも大歓迎です。

ALTER PROCEDURE dbo.GetBondAmounts
(
    @Username varchar(20)
)
AS
DECLARE @Admin AS bit
SELECT @Admin = Admin FROM Users WHERE Username = @Username
if(@Admin = 1)
BEGIN
    RETURN
    SELECT Bond.ID, SUM(Charge.BondAmount) + SUM(ChargeWithoutPower.BondAmount) Amount,
    SUM(Charge.BondPremium) + SUM(ChargeWithoutPower.BondPremium) + SUM(Forfeiture.AmountPaid)
    + SUM(Forfeiture.CostOfApprehension) - SUM(Payment.Amount) - SUM(BalanceForgiveness.AmountForgiven) Balance
    FROM Bond LEFT OUTER JOIN Payment ON Bond.ID = Payment.Bond
    LEFT OUTER JOIN BalanceForgiveness ON Bond.ID = BalanceForgiveness.BondID
    LEFT OUTER JOIN Powers ON Bond.ID = Powers.Bond
    LEFT OUTER JOIN Charge ON Powers.Surety = Charge.PowerSurety
    AND Powers.PowerPrefix = Charge.PowerPrefix AND Powers.PowerNumber = Charge.PowerNumber
    LEFT OUTER JOIN ChargeWithoutPower ON Bond.ID = ChargeWithoutPower.ID
    LEFT OUTER JOIN Forfeiture ON Bond.ID = Forfeiture.Bond
    LEFT OUTER JOIN BondFee ON Bond.ID = BondFee.Bond
    GROUP BY Bond.ID
END
ELSE
BEGIN
    RETURN
    SELECT Bond.ID, SUM(Charge.BondAmount) + SUM(ChargeWithoutPower.BondAmount) Amount,
    SUM(Charge.BondPremium) + SUM(ChargeWithoutPower.BondPremium) + SUM(Forfeiture.AmountPaid)
    + SUM(Forfeiture.CostOfApprehension) - SUM(Payment.Amount) - SUM(BalanceForgiveness.AmountForgiven) Balance
    FROM Bond LEFT OUTER JOIN UserAgency ON Bond.Agency = UserAgency.Agency
    LEFT OUTER JOIN Payment ON Bond.ID = Payment.Bond
    LEFT OUTER JOIN BalanceForgiveness ON Bond.ID = BalanceForgiveness.BondID
    LEFT OUTER JOIN Powers ON Bond.ID = Powers.Bond
    LEFT OUTER JOIN Charge ON Powers.Surety = Charge.PowerSurety
    AND Powers.PowerPrefix = Charge.PowerPrefix AND Powers.PowerNumber = Charge.PowerNumber
    LEFT OUTER JOIN ChargeWithoutPower ON Bond.ID = ChargeWithoutPower.ID
    LEFT OUTER JOIN Forfeiture ON Bond.ID = Forfeiture.Bond
    LEFT OUTER JOIN BondFee ON Bond.ID = BondFee.Bond
    WHERE UserAgency.Username = @Username
    GROUP BY Bond.ID
END

次のように C# を使用してこの手順にアクセスしています。

bondingData.Merge(getBondAmountsAdapter.GetData(currentUser.Username));

これは、結果が返されたかどうかをテストして確認するために追加した行です。

MessageBox.Show("Bond Count A: " + bondingData.Bond.Count.ToString() + "\r\nBond Count B: " + bondingData.GetBondAmounts.Count.ToString());

私がそれを実行したとき、これが私が受け取った結果です:

Bond Count A: 1
Bond Count B: 0

[編集]

OK、これが私の問題に対する私の解決策です:

ALTER PROCEDURE dbo.GetBondAmounts
(
    @Username varchar(20)
)
AS
    SELECT Bond.ID, dbo.GetBondTotal(Bond.ID) Total, dbo.GetBondBalance(Bond.ID) Balance
    FROM Bond LEFT OUTER JOIN
    UserAgency ON Bond.Agency = UserAgency.Agency
    WHERE UserAgency.Username = @Username
    OR EXISTS (SELECT * FROM Users WHERE Username = @Username AND Admin = 1)
    GROUP BY Bond.ID

これらは、ここで呼び出される 2 つのストアド関数です。それがどれほど効率的かはわかりませんが、それほど多くの支払いや個々の債券に関連するものはありません. プログラムに多くのレコードがあると、遅くなる可能性があります。

機能 1:

ALTER FUNCTION dbo.GetBondTotal
(
@BondID bigint
)
RETURNS money
AS
BEGIN
    DECLARE @PowersTotal AS money;
    DECLARE @ChargesTotal AS money;
    DECLARE @BondFeeTotal AS money;
    DECLARE @ForfeitureCosts AS money;
    SELECT @PowersTotal = SUM(BondPremium)
    FROM Charge INNER JOIN Powers ON Charge.PowerSurety = Powers.Surety
    AND Charge.PowerPrefix = Powers.PowerPrefix
    AND Charge.PowerNumber = Powers.PowerNumber
    WHERE Bond = @BondID
    SELECT @ChargesTotal = SUM(BondPremium)
    FROM ChargeWithoutPower
    WHERE BondID = @BondID
    SELECT @BondFeeTotal = SUM(Amount)
    FROM BondFee WHERE Bond = @BondID
    SELECT @ForfeitureCosts = SUM(CostOfApprehension) + SUM(AmountPaid)
    FROM Forfeiture WHERE Bond = @BondID
    RETURN @PowersTotal + @ChargesTotal + @BondFeeTotal + @ForfeitureCosts
END

機能 2:

ALTER FUNCTION dbo.GetBondBalance
(
@BondID bigint
)
RETURNS MONEY
AS
    BEGIN
    DECLARE @PowersTotal AS money;
    DECLARE @ChargesTotal AS money;
    DECLARE @BondFeeTotal AS money;
    DECLARE @ForfeitureCosts AS money;
    DECLARE @PaymentTotal AS money;
    DECLARE @AmountForgiven AS money;
    SELECT @PowersTotal = SUM(BondPremium)
    FROM Charge INNER JOIN Powers ON Charge.PowerSurety = Powers.Surety
    AND Charge.PowerPrefix = Powers.PowerPrefix
    AND Charge.PowerNumber = Powers.PowerNumber
    WHERE Bond = @BondID
    SELECT @ChargesTotal = SUM(BondPremium)
    FROM ChargeWithoutPower
    WHERE BondID = @BondID
    SELECT @BondFeeTotal = SUM(Amount)
    FROM BondFee WHERE Bond = @BondID
    SELECT @ForfeitureCosts = SUM(CostOfApprehension) + SUM(AmountPaid)
    FROM Forfeiture WHERE Bond = @BondID
    SELECT @PaymentTotal = SUM(Amount)
    FROM Payment WHERE Bond = @BondID
    SELECT @AmountForgiven = SUM(AmountForgiven)
    FROM BalanceForgiveness WHERE BondID = @BondID
    RETURN @PowersTotal + @ChargesTotal + @BondFeeTotal + @ForfeitureCosts - @PaymentTotal - @AmountForgiven
END

金額 (Charge.Amount + ChargeWithoutPower.Amount) をカバーする 3 番目の関数を作成すると思います。

4

2 に答える 2

9

RETURN各セクションの冒頭にステートメントがあります。

このステートメントがヒットするとすぐにクエリが返されるため、SELECTは実行されず、何も返されません。

この問題を解決するには、RETURNステートメントを削除します。

于 2012-05-16T15:42:37.410 に答える
3

この手順で結果セットを返す必要があると考えているのではないでしょうか。実際、プロシージャは整数のみを「返す」ことができます。それらは出力パラメータを設定でき、選択された結果セットは出力ストリームに入れられますが、実際には RETURN ステートメントとは何の関係もありません。

また、2 番目のクエリである WHERE 句は、最初の LEFT JOIN を INNER JOIN に効果的に変換することに注意してください。

これら 2 つのクエリを 1 つのクエリに結合すると、メンテナンスがより簡単になり、個別のビット変数と管理クエリの必要性をなくすことができます。結合されたバージョンは保守が容易になる可能性がありますが、2 つのケースでは実際には非常に異なる実行計画が必要になる可能性があるため、最適ではない実行計画になる可能性があることに注意してください。また、パラメータ スニッフィングに対して脆弱である可能性があるため、「不明な場合に最適化」オプションを使用するか、2 つに分割しておくことをお勧めします。これはトレードオフです。多くの場合、1 つの場所で保守する方が簡単な反復コードを目にするときは、それを比較検討する必要があります。繰り返しの結合をビューまたはインライン テーブル値関数に変換できる場合があります。

私はそれがこれに還元されると思います:

ALTER PROCEDURE dbo.GetBondAmounts
(
    @Username varchar(20)
)
AS
BEGIN
    SELECT Bond.ID
           ,SUM(Charge.BondAmount)
             + SUM(ChargeWithoutPower.BondAmount) AS Amount
           ,SUM(Charge.BondPremium)
             + SUM(ChargeWithoutPower.BondPremium)
             + SUM(Forfeiture.AmountPaid)
             + SUM(Forfeiture.CostOfApprehension)
             - SUM(Payment.Amount)
             - SUM(BalanceForgiveness.AmountForgiven) AS Balance
    FROM Bond
    LEFT OUTER JOIN UserAgency ON Bond.Agency = UserAgency.Agency
    LEFT OUTER JOIN Payment ON Bond.ID = Payment.Bond
    LEFT OUTER JOIN BalanceForgiveness ON Bond.ID = BalanceForgiveness.BondID
    LEFT OUTER JOIN Powers ON Bond.ID = Powers.Bond
    LEFT OUTER JOIN Charge ON Powers.Surety = Charge.PowerSurety
        AND Powers.PowerPrefix = Charge.PowerPrefix
        AND Powers.PowerNumber = Charge.PowerNumber
    LEFT OUTER JOIN ChargeWithoutPower ON Bond.ID = ChargeWithoutPower.ID
    LEFT OUTER JOIN Forfeiture ON Bond.ID = Forfeiture.Bond
    LEFT OUTER JOIN BondFee ON Bond.ID = BondFee.Bond
    WHERE UserAgency.Username = @Username
        OR EXISTS (SELECT * FROM Users WHERE Username = @Username AND Admin = 1)
    GROUP BY Bond.ID
END

(簡略化のこの時点で、プロシージャの代わりにインライン テーブル値関数を使用することを強く検討することに注意してください。これは、パラメーター化されたビューに似ており、ストアド プロシージャよりも柔軟です)

あなたがグループ化して参加している方法は少し心配です。

複数の支払いと没収 (または Bond.ID によってリンクされた他のテーブル) が債券に関連付けられている場合、不注意でクロス結合が行われ、値が乗算されてから合計され、誤った結果が得られます。

グループ化を行うには、次のようなことを行う必要があると思います-サブクエリでグループ化を行い、同じ名前でそれらを再エイリアス化したことに注意してください。

ALTER PROCEDURE dbo.GetBondAmounts
(
    @Username varchar(20)
)
AS
BEGIN
    SELECT Bond.ID
           ,Charge.BondAmount
             + ChargeWithoutPower.BondAmount AS Amount
           ,Charge.BondPremium
             + ChargeWithoutPower.BondPremium
             + Forfeiture.AmountPaid
             + Forfeiture.CostOfApprehension
             - Payment.Amount
             - BalanceForgiveness.AmountForgiven AS Balance
    FROM Bond
    LEFT OUTER JOIN UserAgency ON Bond.Agency = UserAgency.Agency
    LEFT OUTER JOIN (
        SELECT Bond, SUM(Amount) AS Amount
        FROM Payment
        GROUP BY Bond
    ) AS Payment ON Bond.ID = Payment.Bond
    --- etc..
    WHERE UserAgency.Username = @Username
        OR EXISTS (SELECT * FROM Users WHERE Username = @Username AND Admin = 1)
END
于 2012-05-16T15:54:40.880 に答える