4

私はMSSQLServer 2008データベースを持っており、そこに食べ物を提供する場所(カフェ、レストラン、ダイナーなど)を保存しています。このデータベースに接続されているWebサイトでは、場所を1から3のスケールで評価できます。

このWebサイトには、特定の都市の上位25(最高評価)の場所を含むトップリストを表示できるページがあります。データベース構造は次のようになります(テーブルにはさらに多くの情報が格納されていますが、関連する情報は次のとおりです)。 データベース構造:都市->場所->投票

場所は都市にあり、投票は場所に行われます。

これまで、特定の場所のすべての投票の合計をその場所の投票数で割った、各場所の平均投票スコアを計算しました。次のようになります(擬似コード)。

vote_count = total number of votes for the place
vote_sum = total sum of all the votes for the place

vote_score = vote_sum/vote_count

また、場所に投票がない場合は、ゼロ除算を処理する必要があります。これはすべて、トップリストに表示したい他のデータをフェッチするストアドプロシージャ内で行われます。投票スコアが最も高い上位25位をフェッチする現在のストアドプロシージャは次のとおりです。

ALTER PROCEDURE [dbo].[GetTopListByCity]
    (
    @city_id Int
    )
AS
    SELECT TOP 25 dbo.Places.place_id, 
           dbo.Places.city_id,
           dbo.Places.place_name,
           dbo.Places.place_alias,
           dbo.Places.place_street_address,
           dbo.Places.place_street_number,
           dbo.Places.place_zip_code,
           dbo.Cities.city_name,
           dbo.Cities.city_alias,
           dbo.Places.place_phone,
           dbo.Places.place_lat,
           dbo.Places.place_lng,
           ISNULL(SUM(dbo.Votes.vote_score),0) AS vote_sum,
           (SELECT COUNT(*) FROM dbo.Votes WHERE dbo.Votes.place_id = dbo.Places.place_id) AS vote_count,
           COALESCE((CONVERT(FLOAT,SUM(dbo.Votes.vote_score))/(CONVERT(FLOAT,(SELECT COUNT(*) FROM dbo.Votes WHERE dbo.Votes.place_id = dbo.Places.place_id)))),0) AS vote_score

    FROM dbo.Places INNER JOIN dbo.Cities ON dbo.Places.city_id = dbo.Cities.city_id
    LEFT OUTER JOIN dbo.Votes ON dbo.Places.place_id = dbo.Votes.place_id
    WHERE dbo.Places.city_id = @city_id
    AND dbo.Places.hidden = 0
    GROUP BY dbo.Places.place_id,
             dbo.Places.city_id,
             dbo.Places.place_name,
             dbo.Places.place_alias,
             dbo.Places.place_street_address,
             dbo.Places.place_street_number,
             dbo.Places.place_zip_code,
             dbo.Cities.city_name,
             dbo.Cities.city_alias,
             dbo.Places.place_phone,
             dbo.Places.place_lat,
             dbo.Places.place_lng
    ORDER BY vote_score DESC, vote_count DESC, place_name ASC

    RETURN

ご覧のとおり、投票スコアだけでなく、場所や場所などのデータが必要です。これは問題なく機能しますが、大きな問題が1つあります。投票数が考慮されていないため、投票スコアが単純すぎることです。簡単な計算方法では、スコア3で1票の場所は、スコア3で14票、スコア2で1票の場所よりもリストの上位になります。

3/1 = 3
(14*3 + 1*2) = 44/15 = 2.933333333333

これを修正するために、私は何らかの形の加重平均/加重インデックスの使用を検討してきました。有望に見える真のベイズ推定の例を見つけました。次のようになります。

weighted rating (WR) = (v ÷ (v+m)) × R + (m ÷ (v+m)) × C

where:

R = average for the place (mean) = (Rating)
v = number of votes for the place = (votes)
m = minimum number of votes required to be listed in the Top 25 (unsure how many, but somewhere between 2-5 seems realistic)
C = the mean vote across the whole database

問題は、この加重評価をストアドプロシージャに実装しようとすると始まります。これはすぐに複雑になり、括弧に絡まって、ストアドプロシージャの機能を追跡できなくなります。

今私は2つの質問でいくつかの助けが必要です:

これは私のサイトの加重指数を計算するのに適した方法ですか?

ストアドプロシージャに実装すると、これ(または別の適切な計算方法)はどのようになりますか?

4

3 に答える 3

1

計算に問題はありません。しかし、私はあなたが何度も同じことをしているのを見ることができます。私の提案は、1つの場所で集計を行うのに役立ち、選択は非常に簡単です。

;WITH CTE
(
    SELECT
        SUM(dbo.Votes.vote_score) AS SumOfVoteScore,
        COUNT(*) AS CountOfVotes,
        Votes.place_id
    FROM
        Votes
    GROUP BY
        Votes.place_id
)
 SELECT TOP 25 
    dbo.Places.place_id, 
    dbo.Places.city_id,
    dbo.Places.place_name,
    dbo.Places.place_alias,
    dbo.Places.place_street_address,
    dbo.Places.place_street_number,
    dbo.Places.place_zip_code,
    dbo.Cities.city_name,
    dbo.Cities.city_alias,
    dbo.Places.place_phone,
    dbo.Places.place_lat,
    dbo.Places.place_lng,
    ISNULL(CTE.SumOfVoteScore,0) AS vote_sum,
    CTE.CountOfVotes AS vote_count,
    COALESCE((CONVERT(FLOAT,CTE.SumOfVoteScore)/
    (CONVERT(FLOAT,CTE.CountOfVotes))),0) AS vote_score

FROM dbo.Places INNER JOIN dbo.Cities ON dbo.Places.city_id = dbo.Cities.city_id
LEFT JOIN CTE ON dbo.Places.place_id=CTE.place_id
WHERE dbo.Places.city_id = @city_id
AND dbo.Places.hidden = 0
GROUP BY dbo.Places.place_id,
         dbo.Places.city_id,
         dbo.Places.place_name,
         dbo.Places.place_alias,
         dbo.Places.place_street_address,
         dbo.Places.place_street_number,
         dbo.Places.place_zip_code,
         dbo.Cities.city_name,
         dbo.Cities.city_alias,
         dbo.Places.place_phone,
         dbo.Places.place_lat,
         dbo.Places.place_lng
ORDER BY vote_score DESC, vote_count DESC, place_name ASC

CTE関数は、計算を再利用するのに役立ちます。SUM(vote_score)を何度も使用する必要がないようにSELECT COUNT(*) FROM Votes WHERE...。したがって、計算を選択するときは、非常に簡単に実行できます。

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

編集

CTEでテーブル列を定義する必要はありません。これはこれCTE (SumOfVoteScore, CountOfVotes, place_id) ASと同じくらいうまく機能しますCTE AS。再帰cteを使用している場合は、列を定義する必要があります。あなたはunion他の部分と一緒にいるからです。

ここここでの参照のために、CTE機能に関するいくつかの情報があります

于 2012-04-02T07:50:25.847 に答える
0

さて、これが私が思いついたストアドプロシージャです:

ALTER PROCEDURE dbo.GetTopListByCityCTE
    (
    @city_id Int
    )
AS

DECLARE @MinimumNumber float;
DECLARE @TotalNumberOfVotes int;
DECLARE @AverageRating float;
DECLARE @AverageNumberOfVotes float;

/* MINIMUM NUMBER */
SET @MinimumNumber = 1;

/* TOTAL NUMBER OF VOTES -- ALL PLACES */
SET @TotalNumberOfVotes = (
    SELECT COUNT(*) FROM Votes
);

/* AVERAGE RATING -- ALL PLACES */
SET @AverageRating = (
    SELECT
        CONVERT(FLOAT,(SUM(dbo.Votes.vote_score))) / CONVERT(FLOAT,COUNT(*)) AS AverageRating
    FROM 
        Votes);

/* AVERAGE NUMBER OF VOTES -- ALL PLACES */
/* CURRENTLY NOT USED IN INDEX - KEPT FOR REFERENCE */
SET @AverageNumberOfVotes = (
    SELECT AVG(CONVERT(FLOAT,NumberOfVotes)) FROM (SELECT COUNT(*) AS NumberOfVotes FROM Votes GROUP BY place_id) AS AverageNumberOfVotes

);
/* SUM OF ALL VOTE SCORES AND COUNT OF ALL VOTES -- INDIVIDUAL PLACES */
WITH CTE AS (
    SELECT
        CONVERT(FLOAT, SUM(dbo.Votes.vote_score)) AS SumVotesForPlace,
        CONVERT(FLOAT, COUNT(*)) AS CountVotesForPlace,
        Votes.place_id
    FROM
        Votes
    GROUP BY
        Votes.place_id
)

 SELECT 
    dbo.Places.place_id, 
    dbo.Places.city_id,
    dbo.Places.place_name,
    dbo.Places.place_alias,
    dbo.Places.place_street_address,
    dbo.Places.place_street_number,
    dbo.Places.place_zip_code,
    dbo.Cities.city_name,
    dbo.Cities.city_alias,
    dbo.Places.place_phone,
    dbo.Places.place_lat,
    dbo.Places.place_lng,
    ISNULL(CTE.SumVotesForPlace,0) AS vote_sum,
    ISNULL(CTE.CountVotesForPlace,0) AS vote_count,
    COALESCE((CTE.SumVotesForPlace/
    CTE.CountVotesForPlace),0) AS vote_score,
    ISNULL((CTE.CountVotesForPlace / (CTE.CountVotesForPlace + @MinimumNumber)) * (COALESCE((CTE.SumVotesForPlace / CTE.CountVotesForPlace),0)) + (@MinimumNumber / (CTE.CountVotesForPlace + @MinimumNumber)) * @AverageRating,0) AS WeightedIndex

FROM dbo.Places INNER JOIN dbo.Cities ON dbo.Places.city_id = dbo.Cities.city_id
LEFT JOIN CTE ON dbo.Places.place_id = CTE.place_id
WHERE dbo.Places.city_id = @city_id
AND dbo.Places.hidden = 0
GROUP BY dbo.Places.place_id,
         dbo.Places.city_id,
         dbo.Places.place_name,
         dbo.Places.place_alias,
         dbo.Places.place_street_address,
         dbo.Places.place_street_number,
         dbo.Places.place_zip_code,
         dbo.Cities.city_name,
         dbo.Cities.city_alias,
         dbo.Places.place_phone,
         dbo.Places.place_lat,
         dbo.Places.place_lng,
         CTE.SumVotesForPlace,
         CTE.CountVotesForPlace
ORDER BY WeightedIndex DESC, vote_count DESC, place_name ASC

計算には使用されない@AverageNumberOfVotesという変数がありますが、必要になった場合に備えて参照用に保持しました。

これをデータに対して実行すると、以前とは少し異なる結果が得られますが、それは革命ではなく、私が必要としていたものではありません。上記のSPを実行したときに返される上位10行は次のとおりです。

vote_sum        vote_count  vote_score          WeightedIndex
1110            409         2,71393643031785    2,7140960047496
807             310         2,60322580645161    2,60449697749787
38              15          2,53333333333333    2,56708633093525
25              10          2,5                 2,55442722744881
2               1           2                   2,55188848920863
2               1           2                   2,55188848920863
2               1           2                   2,55188848920863
2               1           2                   2,55188848920863
2               1           2                   2,55188848920863
2               1           2                   2,55188848920863

ここでの問題は、投票が1つだけで、スコアが2の場合、加重指数が2,55188848920863になるということのようです。

このインデックスの計算式はIMDB(http://www.imdb.com/chart/top)から取得されており、何か間違ったことをしたか、データベースにあるデータが比較できないと思っています。 IMDBが持っているデータ(投票数または投票スケール)に?

編集

この関数を調整して、より適切に機能するようにする方法はありますか?よりうまく機能する別の機能/アプローチはありますか?ストアドプロシージャで計算を行う必要があります。

于 2012-04-02T13:21:31.500 に答える
0

ありがとうアリオン!

私はCTEに沿って何かを探していましたが、それが自分が探しているものだとは知りませんでした。何か新しいことを学ぶのはいつでもいいことであり、他のプロジェクトでCTEを利用することを私は知っています。ストアドプロシージャにCTEを実装すると、次のコードが表示されます。

ALTER PROCEDURE dbo.GetTopListByCityCTE
    (
    @city_id Int
    )
AS

;WITH CTE (SumOfVoteScore, CountOfVotes, place_id) AS
(
    SELECT
        SUM(dbo.Votes.vote_score) AS SumOfVoteScore,
        COUNT(*) AS CountOfVotes,
        Votes.place_id
    FROM
        Votes
    GROUP BY
        Votes.place_id

)

 SELECT TOP 25 
    dbo.Places.place_id, 
    dbo.Places.city_id,
    dbo.Places.place_name,
    dbo.Places.place_alias,
    dbo.Places.place_street_address,
    dbo.Places.place_street_number,
    dbo.Places.place_zip_code,
    dbo.Cities.city_name,
    dbo.Cities.city_alias,
    dbo.Places.place_phone,
    dbo.Places.place_lat,
    dbo.Places.place_lng,
    ISNULL(CTE.SumOfVoteScore,0) AS vote_sum,
    CTE.CountOfVotes AS vote_count,
    COALESCE((CONVERT(FLOAT,CTE.SumOfVoteScore)/
    (CONVERT(FLOAT,CTE.CountOfVotes))),0) AS vote_score

FROM dbo.Places INNER JOIN dbo.Cities ON dbo.Places.city_id = dbo.Cities.city_id
LEFT JOIN CTE ON dbo.Places.place_id = CTE.place_id
WHERE dbo.Places.city_id = @city_id
AND dbo.Places.hidden = 0
GROUP BY dbo.Places.place_id,
         dbo.Places.city_id,
         dbo.Places.place_name,
         dbo.Places.place_alias,
         dbo.Places.place_street_address,
         dbo.Places.place_street_number,
         dbo.Places.place_zip_code,
         dbo.Cities.city_name,
         dbo.Cities.city_alias,
         dbo.Places.place_phone,
         dbo.Places.place_lat,
         dbo.Places.place_lng,
         CTE.SumOfVoteScore,
         CTE.CountOfVotes
ORDER BY vote_score DESC, vote_count DESC, place_name ASC

簡単にチェックすると、前のコードと同じ結果が返されることがわかりますが、読みやすく、従うのがはるかに簡単で、うまくいけばはるかに効率的です。

ここで、古い(単純な)評価計算方法を、投票数を考慮した新しい方法に置き換える実験を行う必要があります。

于 2012-04-02T10:09:14.737 に答える