需要帮助构建性能最佳的SQL查询

时间:2014-04-26 19:52:15

标签: c# sql linq entity

我正在制作一个倾听者,这样我就可以将多个游戏的游戏数据发布到我的服务器上。无论用什么设备,我都会用它来保持客户端的同步!这部分我完成了使用Entity和Linq进行插入并在它们被去同步时加载播放器数据。

服务器的每个网站大约需要2到10毫秒,具体取决于发送的数据。 (我希望没事!)。这主要是FirstOrDefault选择,修改一些数据和SaveChanges。

但服务器还必须使用记分板为客户端提供服务。 "今天/所有时间"和#34;仅限每个人/朋友"。

我尝试了一些Linq SQL查询。但是当我看到输出时,我真的很害怕它会如何预制(有成千上万的用户和数百万的得分记录)。

以下是一个例子:

using (BertEntities BGE = new BertEntities())
{
    var query =
       (from score in BGE.ScoreTable
        join game in BGE.GameSession on score.GameSessionID equals game.ID
        join user in BGE.User on game.UserID equals user.ID
        where
                game.PublicHighScore == true &&
                game.Banned == false &&
                game.GameID == GameID &&
                score.LevelID == level
        orderby score.Score descending

        select new ScoreResult
        {
            ID = user.ID,
            Name = user.FacebookName,
            Score = score.Score,
            Streak = score.Streak,
            Time = score.Time
        }).Skip(skip).Take(count);


    return JsonConvert.SerializeObject(query.ToList());
}

会像这样执行

SELECT TOP (100)
    [Project1].[Score] AS [Score],
    [Project1].[ID] AS [ID],
    [Project1].[FacebookName] AS [FacebookName],
    [Project1].[Streak] AS [Streak],
    [Project1].[Time] AS [Time]
    FROM ( SELECT [Project1].[Score] AS [Score], [Project1].[Streak] AS [Streak], [Project1].[Time] AS [Time], [Project1].[ID] AS [ID], [Project1].[FacebookName] AS [FacebookName], row_number() OVER (ORDER BY [Project1].[Score] ASC) AS [row_number]
        FROM ( SELECT
        [Filter1].[Score] AS [Score],
        [Filter1].[Streak] AS [Streak],
        [Filter1].[Time] AS [Time],
        [Extent3].[ID] AS [ID],
        [Extent3].[FacebookName] AS [FacebookName]
        FROM   (SELECT [Extent1].[LevelID] AS [LevelID], [Extent1].[Score] AS [Score], [Extent1].[Streak] AS [Streak], [Extent1].[Time] AS [Time], [Extent2].[UserID] AS [UserID], [Extent2].[GameID] AS [GameID]
            FROM  [dbo].[ScoreTable] AS [Extent1]
            INNER JOIN [dbo].[GameSession] AS [Extent2] ON [Extent1].[GameSessionID] = [Extent2].[ID]
            WHERE (1 = [Extent2].[PublicHighScore]) AND (0 = [Extent2].[Banned]) ) AS [Filter1]
        INNER JOIN [dbo].[User] AS [Extent3] ON [Filter1].[UserID] = [Extent3].[ID]
        WHERE ([Filter1].[GameID] = @p__linq__0) AND ([Filter1].[LevelID] = @p__linq__1)
    )  AS [Project1]
)  AS [Project1]
WHERE [Project1].[row_number] > 0
ORDER BY [Project1].[Score] DESC

我没有SQL专家,但对我来说这看起来很难。 (考虑到我希望这可以扩展,即使有数百万条记录和数千条请求而不会杀死服务器。)

这是数据库布局: www.invokergame.com/db.png

我想在ScoreTable表中存储我需要的所有数据,因此我避免进行连接。但这会导致大量重复数据,而且我很确定这是错误的方法。

也许我应该忘记Entity Linq,并将其写入ADO.NET SQL(我需要帮助)?

对于文字墙感到抱歉,我期待着一些明智的话语!

修改

好的TomTom,这是你伪造的吗?将我需要的所有数据拖到一个数据集中。然后使用LINQ更新或删除此列表中的项目,当用户创建新的高分时,或禁用PublicHighScore?

这将如何表现?

public class ScoreCacheItem
{
    public Int64 ScoreTableID { get; set; }
    public Guid GameID { get; set; }
    public int LevelID { get; set; }
    public Int64 UserID { get; set; }
    public string Name { get; set; }
    public int Score { get; set; }
    public int Streak { get; set; }
    public double Time { get; set; }
    public DateTime Today { get; set; }
    public int Today_Score { get; set; }
    public int Today_Streak { get; set; }
    public double Today_Time { get; set; }
}


public sealed class ScoreCacheSystem
{
    private List<ScoreCacheItem> FScoreCache = new List<ScoreCacheItem>();
    private object FLock = new object();

    private List<ScoreCacheItem> LoadAllScores()
    {
        using (BertEntities BGE = new BertEntities())
        {
            var query =
               (from score in BGE.ScoreTable
                join game in BGE.GameSession on score.GameSessionID equals game.ID
                join user in BGE.User on game.UserID equals user.ID
                where
                        game.PublicHighScore == true &&
                        game.Banned == false 
                orderby score.Score descending

                select new ScoreCacheItem
                {
                    ScoreTableID = score.ID,
                    GameID = game.GameID,
                    LevelID = score.LevelID,
                    UserID = user.ID,
                    Name = user.FacebookName,
                    Score = score.Score,
                    Streak = score.Streak,
                    Time = score.Time,
                    Today = score.Today,
                    Today_Score = score.Today_Score,
                    Today_Streak = score.Today_Streak,
                    Today_Time = score.Today_Time
                });
            return query.ToList();
        }
    }

    public List<ScoreCacheItem> HighScores {
        get
        {
            lock (FLock)
            {
                if (FScoreCache == null)
                {
                    FScoreCache = LoadAllScores();
                }
                return FScoreCache;
            }
        }
    }
}

1 个答案:

答案 0 :(得分:0)

您在SQL方面遇到了严重问题。 EF做了相当不错的SQL - 但在这里我认为一般使用SQL Server是不好的。

如果您需要以超快的速度回答这种类型的查询,那么请从内存中执行此操作 - 而不是从数据库服务器执行。内存中查找对于对数据分发重复且时间敏感的简单查询是典型的(金融交易 - 没有人会将价格写入数据库然后让客户端拉出它们;在将它们写入商店时分发它们)。 p>

否则你就可以伪造它 - 我想到了缓存。使用此体系结构优化对重要数据库服务器的要求,您无法做很多事情。确保您的指数到位。从查询计划开始工作,确保您不会遗漏任何内容。

但实际上,尽量避免点击SQL Server - 所有级别的缓存输出。而且,SQL非常简单,BTW。当您点击2-3个屏幕长分析查询时,事情变得有趣;)数百万条记录不是问题,主要问题是并发请求。连接最好具有读取提交的隔离,以确保您不设置锁定。然后获得一个合适的服务器并扩展,除非你改变架构。

  

我想在ScoreTable表中存储我需要的所有数据,所以   我避免加入。但这会导致很多重复   数据,我很确定这是错误的方法。

绝对不是 - 不是错误的做法。非规范化在读取繁重的报告数据库中是典型的。您可以使用触发器自动维护此表。在您的情况下是否有意义必须使用良好的数据集进行测试。数据仓库中的标准做法通常不是很糟糕,尽管这些通常涉及实际数据量(数十亿行,而不是像一百万或两行一样微不足道)。在我上一次大型数据仓库项目中,我们每天要加载大约4.​​5亿行 - 并且必须将它们归档10年才能进行报告。查找&#34; Star Schema&#34;在谷歌上。基本只读/数据仓库方案的规则是不同的。