enb*_*081 26 .net c# sql asp.net sql-server-2008
我正在使用C#和Sql Server 2008开发一个ASP.NET项目.
我有三张桌子:

每个用户对每个数据字段都有一个特定值,该值存储在DataFieldsValues中.
现在我想显示一个如下所示的报告:

我创建了对象User,和DataField.在DataField对象中,有Method string GetValue(User user),我在其中获取某个用户的字段值.
然后我有用户List<User> users列表和DataFields列表,List<DataField> fields我执行以下操作:
string html = string.Empty;
html += "<table>";
html += "<tr><th>Username</th>";
foreach (DataField f in fields)
{
html += "<th>" + f.Name + "</th>";
}
html += "</tr>"
foreach (User u in users)
{
html += "<tr><td>" + u.Username + "</td>"
foreach (DataField f in fields)
{
html += "<td>" + f.GetValue(u) + "</td>";
}
html += "</tr>"
}
Response.Write(html);
Run Code Online (Sandbox Code Playgroud)
这工作正常,但它非常慢,我说的是20个用户和10个数据字段.在性能方面有没有更好的方法来实现这一目标?
编辑:对于类中的每个参数,我使用以下方法检索值:
public static string GetDataFromDB(string query)
{
string return_value = string.Empty;
SqlConnection sql_conn;
sql_conn = new SqlConnection(ConfigurationManager.ConnectionStrings["XXXX"].ToString());
sql_conn.Open();
SqlCommand com = new SqlCommand(query, sql_conn);
//if (com.ExecuteScalar() != null)
try
{
return_value = com.ExecuteScalar().ToString();
}
catch (Exception x)
{
}
sql_conn.Close();
return return_value;
}
Run Code Online (Sandbox Code Playgroud)
例如:
public User(int _Id)
{
this.Id = _Id
this.Username = DBAccess.GetDataFromDB("select Username from Users where Id=" + this.Id)
//...
}
Run Code Online (Sandbox Code Playgroud)
Chr*_*yva 22
以下是2条有用的建议.第一个建议是显着改善您的表现.第二个建议也有帮助,但可能不会让你的应用程序更快.
你GetDataFromDB(string query)经常调用这个方法.这很糟糕,因为每次都会创建一个新的SqlConnection和SqlCommand.这需要时间和资源.此外,如果有任何网络延迟,则乘以您正在进行的呼叫数.所以这只是个坏主意.
我建议您调用该方法一次并让它填充一个类似的集合,Dictionary<int, string>以便您可以从用户标识键快速查找您的用户名值.
像这样:
// In the DataField class, have this code.
// This method will query the database for all usernames and user ids and
// return a Dictionary<int, string> where the key is the Id and the value is the
// username. Make this a global variable within the DataField class.
Dictionary<int, string> usernameDict = GetDataFromDB("select id, username from Users");
// Then in the GetValue(int userId) method, do this:
public string GetValue(int userId)
{
// Add some error handling and whatnot.
// And a better name for this method is GetUsername(int userId)
return this.usernameDict[userId];
}
Run Code Online (Sandbox Code Playgroud)
这是另一种可以改进的方法,虽然在这种情况下略有改进 - 使用StringBuilder该类.性能显着提升(这里是概述:http://support.microsoft.com/kb/306822).
SringBuilder sb = new StringBuilder();
sb.Append("<table><tr><th>Username</th>");
foreach (DataField f in fields)
{
sb.Append("<th>" + f.Name + "</th>");
}
// Then, when you need the string
string html = sb.ToString();
Run Code Online (Sandbox Code Playgroud)
如果您需要更多说明,请告诉我,但您要求的是非常可行的.我们可以解决这个问题!
如果您进行这两项简单的更改,您将获得出色的表现.我保证.
Rem*_*anu 18
您选择的数据库设计名为Entity-Attribute-Value,这是一种以其性能问题而闻名的设计.SQL Server团队发布了一份关于EAV设计指南的白皮书,请参阅针对性能和可伸缩性的语义数据建模的最佳实践.
唉,你已经有了设计,你现在可以做些什么呢?重要的是将对dB的miriad调用减少到单个调用,并执行一个面向集合的语句来检索数据.游戏名称是表值参数:
declare @users as UsersType;
insert into @users (UserId) values (7), (42), (89);
select ut.Id,
ut.Username,
df.Name as DataFieldName,
dfv.Value
from Users ut
join @users up on ut.Id = up.UserId
join DataFieldValues dfv on ut.Id = dfv.UserId
join DataFields df on dfv.DataFieldId = df.Id
order by ut.Id;
Run Code Online (Sandbox Code Playgroud)
有关完整示例,请参阅此SqlFiddle.
虽然严格来说,可以使用PIVOT运算符检索所需形状的结果(数据字段名称转换为列名称),但我强烈建议不要这样做.PIVOT本身就是一个性能泥潭,但是当你添加所需结果集的动态性质时,基本上不可能将它拉下来.由一行每个属性组成的传统结果集很容易解析为表,因为用户标识所需的顺序保证了相关属性集之间的干净中断.
Kas*_*ols 14
这很慢,因为在引擎盖下,您对数据库进行了20 x 10 = 200次查询.正确的方法是一次加载所有东西.
您应该发布有关加载数据方式的一些详细信息.如果您使用的是Entity Framework,则应使用Include命令使用名为Eager Loading的内容.
// Load all blogs and related posts
var blogs1 = context.Blogs
.Include(b => b.Posts)
.ToList();
Run Code Online (Sandbox Code Playgroud)
一些示例可以在这里找到:http://msdn.microsoft.com/en-us/data/jj574232.aspx
编辑:
您似乎没有使用.NET Framework提供的工具.如今,您不必为自己喜欢的简单风格做自己的数据库访问.此外,您应该像以前一样避免连接字符串HTML.
我建议你使用现有的ASP.NET控件和Entity Framework重新设计你的应用程序.
以下是为您提供分步说明的示例:http: //www.codeproject.com/Articles/363040/An-Introduction-to-Entity-Framework-for-Absolute-B
正如Remus Rusanu所说,你可以使用PIVOT关系运算符以你需要的格式获得你想要的数据,就PIVOT的性能而言,我发现它将取决于你的表的索引和变化和数据集的大小.我非常有兴趣听取他对PIVOTs的看法,因为我们都在这里学习.有PIVOT上的大讨论VS JOINS 这里.
如果DataFields表是静态集,那么您可能不需要担心动态生成SQL,并且可以自己构建存储过程; 如果确实有所不同,您可能需要考虑动态SQL的性能损失(这是一篇很好的文章)或使用不同的方法.
除非您进一步需要数据,否则尝试将返回的集合保持在显示所需的最小值,这是减少开销的好方法,因为除非您的数据库与Web服务器位于同一物理服务器上,否则所有内容都需要通过网络.
确保尽可能少地执行单独的数据调用,这将减少用于提升和断开连接的时间.
当循环控件基于(可能是相关的?)数据集时,你应该总是仔细检查循环内的数据调用,因为这会尖叫JOIN.
当您尝试使用SQL时,尝试熟悉执行计划,这些将帮助您找出运行速度慢的原因查看这些资源以获取更多信息.
无论你接近什么,你决定你需要弄清楚你的代码中的瓶颈在哪里,基本的步骤可以帮助你解决这个问题,因为它可以帮助你自己查看问题所在,这也可以让你识别为你自己可能的方法问题,并建立良好的设计选择习惯.
马克碎石有一些有趣的点,做出关于C#数据读取这里的文章是有点老,但值得一读.
PIVOTing您的数据.(抱歉Remus ;-)) 基于您提供的数据示例,以下代码将获得您所需的内容,而不会进行查询内递归:
--Test Data
DECLARE @Users AS TABLE ( Id int
, Username VARCHAR(50)
, Name VARCHAR(50)
, Email VARCHAR(50)
, [Role] INT --Avoid reserved words for column names.
, Active INT --If this is only ever going to be 0 or 1 it should be a bit.
);
DECLARE @DataFields AS TABLE ( Id int
, Name VARCHAR(50)
, [Type] INT --Avoid reserved words for column names.
);
DECLARE @DataFieldsValues AS TABLE ( Id int
, UserId int
, DataFieldId int
, Value VARCHAR(50)
);
INSERT INTO @users ( Id
, Username
, Name
, Email
, [Role]
, Active)
VALUES (1,'enb081','enb081','enb081@mack.com',2,1),
(2,'Mack','Mack','mack@mack.com',1,1),
(3,'Bob','Bobby','bob@mack.com',1,0)
INSERT INTO @DataFields
( Id
, Name
, [Type])
VALUES (1,'DataField1',3),
(2,'DataField2',1),
(3,'DataField3',2),
(4,'DataField4',0)
INSERT INTO @DataFieldsValues
( Id
, UserId
, DataFieldId
, Value)
VALUES (1,1,1,'value11'),
(2,1,2,'value12'),
(3,1,3,'value13'),
(4,1,4,'value14'),
(5,2,1,'value21'),
(6,2,2,'value22'),
(7,2,3,'value23'),
(8,2,4,'value24')
--Query
SELECT *
FROM
( SELECT ut.Username,
df.Name as DataFieldName,
dfv.Value
FROM @Users ut
INNER JOIN @DataFieldsValues dfv
ON ut.Id = dfv.UserId
INNER JOIN @DataFields df
ON dfv.DataFieldId = df.Id) src
PIVOT
( MIN(Value) FOR DataFieldName IN (DataField1, DataField2, DataField3, DataField4)) pvt
--Results
Username DataField1 DataField2 DataField3 DataField4
enb081 value11 value12 value13 value14
Mack value21 value22 value23 value24
Run Code Online (Sandbox Code Playgroud)
要记住的最重要的事情是为自己尝试一些事情,因为我们建议的任何内容可能会被您所不知道的网站因素所改变.
你是如何访问数据库的?例如,如果您使用EF,请使用Profiler从这些查询中检查生成的SQL.不要每次都在foreach循环中建立连接.
我也不会在服务器端构建html.只需返回页面数据源控件的对象.
确保您没有为每个循环建立数据库连接.
我可以看到,f.GetValue(u)部分是一个返回从数据库中获取的字符串值的方法.
将数据一次性放入一个对象中并执行与f.GetValue(u)在此处所做的相同的操作.
| 归档时间: |
|
| 查看次数: |
36047 次 |
| 最近记录: |