交叉表的简单实现1:使用存储过程

有位朋友要求帮忙实现交叉表(将某些行变为列),之前虽然实现过,但没有整理,今天顺便整理一下,便于自己以后参考,希望对其他网友有帮助,欢迎指出不足之处^_^

数据库中原始数据类似:
fs.GIF
要求前台显示为:
cs.GIF

建立表结构:

None.gif CREATE   TABLE   [ dbo ] . [ t_Score ]  (
None.gif    
[ ScoreId ]   [ int ]   IDENTITY  ( 1 1 NOT   NULL  ,
None.gif    
[ SubjectName ]   [ varchar ]  ( 50 NOT   NULL  ,
None.gif    
[ StudentName ]   [ varchar ]  ( 10 NOT   NULL  ,
None.gif    
[ ScoreValue ]   [ real ]   NOT   NULL  ,
None.gif    
[ ExamDate ]   [ datetime ]   NOT   NULL  
None.gif)

样本数据

None.gif INSERT   INTO  t_Score(SubjectName, StudentName, ScoreValue, ExamDate)
None.gif
VALUES ( ' C语言 ' ' 孙光 ' 80 ' 2006-01-05 ' )
None.gif
None.gif
INSERT   INTO  t_Score(SubjectName, StudentName, ScoreValue, ExamDate)
None.gif
VALUES ( ' 日语 ' ' 孙光 ' 79 ' 2006-07-06 ' )
None.gif
None.gif
INSERT   INTO  t_Score(SubjectName, StudentName, ScoreValue, ExamDate)
None.gif
VALUES ( ' C语言 ' ' 孙光 ' 89 ' 2006-08-09 ' )
None.gif
None.gif
INSERT   INTO  t_Score(SubjectName, StudentName, ScoreValue, ExamDate)
None.gif
VALUES ( ' 英语 ' ' 王二 ' 77 ' 2006-09-10 ' )
None.gif
None.gif
INSERT   INTO  t_Score(SubjectName, StudentName, ScoreValue, ExamDate)
None.gif
VALUES ( ' 英语 ' ' 孙光 ' 77 ' 2006-07-06 ' )
None.gif
None.gif
INSERT   INTO  t_Score(SubjectName, StudentName, ScoreValue, ExamDate)
None.gif
VALUES ( ' C语言 ' ' 王二 ' 89 ' 2006-08-09 ' )


1。通过存储过程实现
实现要点:
a. 使用一个临时表(#ScoreTbl)存储指定学生的所有成绩(根据实际情况,可能有更多条件)
说明:这里无法使用表变量,因为表变量无法用于动态SQL语句中。

b.临时表#ScoreTbl中使用一个平均值标志位,因为将每个科目的平均值计算出来之后也存入了#ScoreTbl
当然,根据需求,还可以增加总成绩标志位等等

c.使用递归的SELECT语句创建动态 DateExam(考试时间)列(参考:Paul Nielsen 的 Microsoft SQL Server 2000宝典 P353 12-7 递归的Select变量)
注意:这里递归成的 @sql  语句有个潜在的bug,就是@sql长度必须小于8000个字符(如果使用sq_executeSql执行动态语句,必须声名为nvarchar,则只能使用4000个字符)
邹建大哥的 化解字符串不能超过8000的方法及交叉表的处理 讨论了三种可选择的方案。
本示例不做此讨论,只求实现交叉表^_^

d.使用CASE表达式选择 DateExam 对应的 ScoreValue(成绩值)(参考:Paul Nielsen 的 Microsoft SQL Server 2000宝典 P353 12-7 -2 动态交叉表查询)

e.使用聚合函数(这里使用SUM)包含ScoreValue列,因为ScoreValue未出现在GROUP BY子句中


具体实现:

None.gif ALTER   PROC  usp_GetCrossScore4(
None.gif
@StuName   varchar ( 10 )
None.gif)
None.gif
AS
None.gif
--  creates a temp table to hold the score records
None.gif
CREATE   TABLE  #ScoreTbl(
None.gifScoreId 
int ,
None.gifSubjectName 
varchar ( 50 ),
None.gifStudentName 
varchar ( 10 ),
None.gifScoreValue 
real ,
None.gifExamDate 
datetime ,
None.gifAvgFlag 
bit   DEFAULT ( 0 )     --  marks as the average of some subject
None.gif
)
None.gif
--  populates basic data of some student
None.gif
INSERT   INTO  #ScoreTbl(ScoreId, SubjectName, StudentName, ScoreValue, ExamDate)
None.gif
SELECT  ScoreId, SubjectName, StudentName, ScoreValue, ExamDate  FROM  t_Score s  WHERE  s.StudentName  =   @StuName
None.gif
--  calculates total for per subject and appends to the temp table
None.gif
INSERT   INTO  #ScoreTbl(ScoreId, SubjectName, StudentName, ScoreValue, ExamDate, AvgFlag)
None.gif
SELECT   NULL , SubjectName, StudentName,  AVG (ScoreValue),  NULL 1   FROM  #ScoreTbl s  GROUP   BY  SubjectName, StudentName
None.gif
-- SELECT * FROM #ScoreTbl
None.gif

None.gif
DECLARE   @Sql   varchar ( 8000 )
None.gif
--  NOTE: some known bug -> you make sure the length of the dynamical sql is less than 8000.
None.gif--
    fortunately, Mr Zou have made a deep discussion againt it at http://blog.csdn.net/zjcxc/archive/2003/12/29/20075.aspx.
None.gif
SET   @Sql   =   ' SELECT SubjectName 科目 '
None.gif
SELECT   @sql   =   @sql   +   ' , SUM(CASE ExamDate WHEN  '''   +   CONVERT ( varchar ( 20 ), ExamDate,  102 +   '''  THEN ScoreValue ELSE NULL END) '''   +   CAST ( YEAR (ExamDate)  AS   VARCHAR +   ' '   +   CAST ( MONTH (ExamDate)  AS   VARCHAR +   ' '   +   CAST ( DAY (ExamDate)  AS   VARCHAR +   ' '''
None.gif
FROM  ( SELECT   DISTINCT  ExamDate  FROM  t_Score s  WHERE  s.StudentName  =   @StuName ) ss
None.gif
SET   @Sql   =   @Sql   +   ' , SUM(CASE AvgFlag WHEN 1 THEN ScoreValue ELSE NULL END)  '' 平均分 '''
None.gif
-- PRINT @Sql
None.gif--
 runs the dynamical sql statement
None.gif
EXEC ( @sql + '  FROM #ScoreTbl s GROUP BY SubjectName ' )
None.gif
ALTER   PROC  usp_GetCrossScore4(
None.gif
@StuName   varchar ( 10 )
None.gif)
None.gif
AS
None.gif
--  creates a temp table to hold the score records
None.gif
CREATE   TABLE  #ScoreTbl(
None.gifScoreId 
int ,
None.gifSubjectName 
varchar ( 50 ),
None.gifStudentName 
varchar ( 10 ),
None.gifScoreValue 
real ,
None.gifExamDate 
datetime ,
None.gifAvgFlag 
bit   DEFAULT ( 0 )     --  marks as the average of some subject
None.gif
)
None.gif
--  populates basic data of some student
None.gif
INSERT   INTO  #ScoreTbl(ScoreId, SubjectName, StudentName, ScoreValue, ExamDate)
None.gif
SELECT  ScoreId, SubjectName, StudentName, ScoreValue, ExamDate  FROM  t_Score s  WHERE  s.StudentName  =   @StuName
None.gif
--  calculates total for per subject and appends to the temp table
None.gif
INSERT   INTO  #ScoreTbl(ScoreId, SubjectName, StudentName, ScoreValue, ExamDate, AvgFlag)
None.gif
SELECT   NULL , SubjectName, StudentName,  AVG (ScoreValue),  NULL 1   FROM  #ScoreTbl s  GROUP   BY  SubjectName, StudentName
None.gif
-- SELECT * FROM #ScoreTbl
None.gif

None.gif
DECLARE   @Sql   varchar ( 8000 )
None.gif
--  NOTE: some known bug -> you make sure the length of the dynamical sql is less than 8000.
None.gif--
    fortunately, Mr Zou have made a deep discussion againt it at http://blog.csdn.net/zjcxc/archive/2003/12/29/20075.aspx.
None.gif
SET   @Sql   =   ' SELECT SubjectName 科目 '
None.gif
SELECT   @sql   =   @sql   +   ' , SUM(CASE ExamDate WHEN  '''   +   CONVERT ( varchar ( 20 ), ExamDate,  102 +   '''  THEN ScoreValue ELSE NULL END) '''   +   CAST ( YEAR (ExamDate)  AS   VARCHAR +   ' '   +   CAST ( MONTH (ExamDate)  AS   VARCHAR +   ' '   +   CAST ( DAY (ExamDate)  AS   VARCHAR +   ' '''
None.gif
FROM  ( SELECT   DISTINCT  ExamDate  FROM  t_Score s  WHERE  s.StudentName  =   @StuName ) ss
None.gif
SET   @Sql   =   @Sql   +   ' , SUM(CASE AvgFlag WHEN 1 THEN ScoreValue ELSE NULL END)  '' 平均分 '''
None.gif
-- PRINT @Sql
None.gif--
 runs the dynamical sql statement
None.gif
EXEC ( @sql + '  FROM #ScoreTbl s GROUP BY SubjectName ' )

测试代码:

ContractedBlock.gif ExpandedBlockStart.gif
None.gif<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
ExpandedBlockStart.gifContractedBlock.gif
<script type="text/C#" runat="server">dot.gif
InBlock.gif    
void Page_Load(object sender, EventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
if (!IsPostBack) dot.gif{
InBlock.gif            LoadScoreData();   
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
void drpStu_SelectedIndexChanged(object sender, EventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{        
InBlock.gif        LoadScoreData();
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
void LoadScoreData()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        string stuName 
= drpStu.SelectedValue;
InBlock.gif        string connStr 
= "SERVER=.;DATABASE=DemoLib;UID=sa";
InBlock.gif        string spName 
= "usp_GetCrossScore4";
InBlock.gif        DataSet ds 
= new DataSet();
InBlock.gif        SqlDataAdapter da 
= new SqlDataAdapter(spName, connStr);
InBlock.gif        da.SelectCommand.CommandType 
= CommandType.StoredProcedure;
InBlock.gif        da.SelectCommand.Parameters.Add(
"@StuName", stuName);
InBlock.gif        da.Fill(ds, 
"CrossScore");
InBlock.gif
InBlock.gif        da.SelectCommand.CommandType 
= CommandType.Text;
InBlock.gif        da.SelectCommand.CommandText 
= "SELECT SubjectName, StudentName, ScoreValue, ExamDate FROM t_Score WHERE StudentName = @StuName";
InBlock.gif        da.Fill(ds, 
"FlatScore");
InBlock.gif        
InBlock.gif        grdCrossScore.DataSource 
= ds.Tables[0];
InBlock.gif        grdCrossScore.DataBind();
InBlock.gif
InBlock.gif        grdFlatScore.DataSource 
= ds.Tables[1];
InBlock.gif        grdFlatScore.DataBind();
ExpandedBlockEnd.gif    }

None.gif
</script>
None.gif
<html xmlns="http://www.w3.org/1999/xhtml" >
None.gif
<head runat="server">
None.gif    
<title>Cross Table Demo</title>
None.gif
</head>
None.gif
<body>
None.gif    
<form id="form1" runat="server">
None.gif    
<div>
None.gif    请选择:
<asp:DropDownList ID="drpStu" OnSelectedIndexChanged="drpStu_SelectedIndexChanged" AutoPostBack="true" runat="server">
None.gif        
<asp:ListItem Value="孙光">孙光</asp:ListItem>
None.gif        
<asp:ListItem Value="王二">王二</asp:ListItem>
None.gif    
</asp:DropDownList>
None.gif    
<br />
None.gif    
<b>Cross:</b>
None.gif    
<asp:DataGrid ID="grdCrossScore" runat="server" BorderColor="SteelBlue" BorderWidth="1px" CellPadding="4" ForeColor="#333333" >
None.gif        
<FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
None.gif        
<EditItemStyle BackColor="#2461BF" />
None.gif        
<SelectedItemStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
None.gif        
<PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
None.gif        
<AlternatingItemStyle BackColor="White" />
None.gif        
<ItemStyle BackColor="#EFF3FB" />
None.gif        
<HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
None.gif    
</asp:DataGrid>    
None.gif    
<br />
None.gif    
<b>Flat:</b>
None.gif     
<asp:DataGrid ID="grdFlatScore" runat="server" BorderColor="#336699" BorderWidth="1px" CellPadding="4" ForeColor="#333333" AutoGenerateColumns="False" >
None.gif        
<FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
None.gif        
<EditItemStyle BackColor="#999999" />
None.gif        
<SelectedItemStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />
None.gif        
<PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Center" />
None.gif        
<AlternatingItemStyle BackColor="White" ForeColor="#284775" />
None.gif        
<ItemStyle BackColor="#F7F6F3" ForeColor="#333333" />
None.gif        
<HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
None.gif         
<Columns>
None.gif             
<asp:BoundColumn DataField="SubjectName" HeaderText="科目"></asp:BoundColumn>
None.gif             
<asp:BoundColumn DataField="ScoreValue" HeaderText="成绩"></asp:BoundColumn>
None.gif             
<asp:BoundColumn DataField="ExamDate" DataFormatString="{0:yyyy年M月dd日}" HeaderText="考试时间"></asp:BoundColumn>             
None.gif         
</Columns>
None.gif    
</asp:DataGrid> 
None.gif    
<asp:GridView ID="GridView1" runat="server">
None.gif        
<Columns>
None.gif        
<asp:BoundField HtmlEncode="false" ApplyFormatInEditMode="true" DataField="ExamDate" DataFormatString="{0:dd/MM/yyyy}" HeaderText="ExamDate" />
None.gif        
</Columns>                
None.gif    
</asp:GridView>
None.gif    
</div>
None.gif    
</form>
None.gif
</body>
None.gif
</html>

效果:


源码:
下载

 下篇介绍如何使用程序动态构造交叉表。
交叉表的简单实现2:使用前端程序实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值