有一大堆的供應商的庫存數據要按產品按供應商按時間段進行數據比較,同時產生圖表,就是
利用zedGraph實現不定曲線數曲線的生成
還是貼張圖來說一下吧。
其它的實現方式,我會在這個項目完成後總結出來,困擾我的地方在於:
如何通過所選擇的供應商數目及時間段進行繪製圖表呢?這裏是供應商數目不定,時間段不定。
關於:ZedGraph這東西我一直使用,可以要網上搜索到關於生成動態曲線(不定曲線數)的文章幾乎沒有。看來只能靠自己了。
返過頭來看一下:要實現上面這玩藝,需要了解這些知識點
1.C# 交互數組的使用
(在最初的時候我選擇了交互數組,但在中間過程中我又覺得自己錯了,改回使用二維數組去實現,儘管數據是KO了,但就是不能畫出圖來,最後認真瞭解了一下C#數組的用法,還是走回原來的路使用了交互數組並達到想要的效果)
2.存儲過程中相關的統計語句,主要是(行轉列 ,Group by, 排序,動態SQL的構建)
gridview中的數據是通過存儲過程去實現獲取的,而這個存儲過程是通過動態SQL去實現的。這個我稍後會整出來。
3.Split的用法,C#數組length的注意
事實證明到實現之前的時候,主要都是一些細枝末節的錯誤,看來我的基本功還不紮實啊!
現在我們來看一下實現的方式:
Step1.首先,我們通過一個頁面進入供應商的比較
Step2.根據所選擇的對象進行數據比較
這裏的一個知識點在於,如何將選擇的對象的ID值給傳出去呢? 這就要用到Split的用法了。
int numOfChecked = 0; //計算選中的對象個數
for (int i = 0; i < this.gdvCompareObject.Rows.Count; i++)
{
bool isChecked = ((CheckBox)gdvCompareObject.Rows[i].FindControl("chkSelected")).Checked;
if (isChecked)
{
numOfChecked++;
}
} //這裏是累加所選擇的對象個數
if (numOfChecked < 2) //進行個數判斷
{
Page.ClientScript.RegisterStartupScript(Page.GetType(), "", "<SCRIPT>alert('很抱歉,你至少要選擇二個以上的對象才能進行比較.');</SCRIPT>");
}
else
{
string compareObjectList = string.Empty;
for (int i = 0; i < gdvCompareObject.Rows.Count; i++)
{
CheckBox chkSelectedAgentID = (CheckBox)gdvCompareObject.Rows[i].FindControl("chkSelected");
if (chkSelectedAgentID.Checked == true)
{
compareObjectList += gdvCompareObject.DataKeys[i]["Object_ID"] + ","; //獲取要比較的對象ID值串成一個字付串
}
}
string URL = "SysSCompareBycompareObjectListAndDate.aspx?AID=" + compareObjectList.Substring(0, compareObjectList.Length - 1); //注意要去掉最後一個字符逗號
Response.Redirect(URL);
}
Step3.把值傳了過來,就可以有文章可以做了。
這裏就要用到動態SQL語句的構造了。這個存儲過程是這樣子的
Create procedure CompareStorageByObjectListAndCalculateDate
@compareObjectID varchar(1000),
@begindate datetime,
@endDate datetime
as
declare @sql varchar(8000)
set @sql = 'select Object_ID,Ojbect_Code '
select @sql = @sql + ' , max(case Convert(Varchar(10),calculateDate,120) when ''' + Convert(Varchar(10),calculateDate,120) + ''' then stockNum else 0 end) [' + Convert(Varchar(10),calculateDate,120) + ']'
from (select distinct calculateDate from Dezai_Storage where calculateDate between @begindate and @endDate ) as a
set @sql = @sql + ' from Dezai_Storage where Object_ID in ('+@compareObjectID+') group by Object_ID,Ojbect_Code'
exec(@sql)
GO
這裏說明一點的是:我們如果用一般的sum(),group by 就可能不能獲取下圖中所要的樣子了.(如果你把時間段都設成字段那另當別論,這裏的時間是動態的)當然,所得到的數據可能是一樣。這裏就需要實現行轉列的要求了。
推薦大家看這篇文章,至少我是受益非淺了
SQL行列轉換大全
http://www.dezai.cn/article_show.asp?ArticleID=32432
Step4:生成數據及圖表
Gridview數據的填充就不囉嗦了,這裏要注意的是這個gridview裏面的字段名及行數據都是動態生成的,因此要將其屬性AutoGenerateColumn設置爲True,其它就不用管了。
如何得到傳過來的供應商的值呢?
獲取代理商ID數組
string agentIDList = PSS.COMMON.Encrypt.Decode(Request.QueryString["AID"].ToString().Trim());
string[] agentID = agentIDList.Split(',');
int agentNum = agentID.Length; //要比較的代理商數量
我這裏主要想說的是zedGraph,及如何可以動態獲取單元格的值。
這裏要獲取值應該有兩種方式,但獲取值都不可避免要用到C#數組尤其是交互數組的操作.
對於C#數組的用法,可以看看下面這幾篇文章
C#中二維數組
http://www.dezai.cn/article_show.asp?ArticleID=32403
二維數組簡單例子(取得行數列數)
http://www.dezai.cn/article_show.asp?ArticleID=32383
C#數組教程
http://www.dezai.cn/article_show.asp?ArticleID=32433
(1)直接從業務層中獲取數據
這種方法我回過頭來看,應該也是可取的。我一開始採用此方法的時候,忽略了數組的長度(就是數組上標 下標)導致我誤以爲這樣獲取值比從gridview中獲取取更加麻煩,這個方法我就沒有實現下去
(2)從gridview中獲取數據填充交互數組
這裏關鍵就是如何把gridview中的數據填充到交互數組中去,因爲zegraph繪製曲線圖時不支持交互數組,所以用交互數組是實現的最好方式。
這裏也有幾個關鍵點,與大家分享一下:
Attention:
A.爲什麼要減2呢?
因爲生成的gridview中的數據,前兩列的數據是供應商的ID和供應商代碼,所以它不是要比較的數據,換句話說他不是整數或具體要比較的數,所以去掉,讓數組的長度能與所得到的數據進行比較
B. xrowCount ,yColumnCount是什麼?
xrowCount是gridview的行數 yColumn是列數. xrowCount不包括表頭,只是數據的行數
C.如何獲取gridview中的各列的列名
我這裏是X軸的刻度實現,也就是獲取列名(除去前兩列)
D.zedgraph的LineItem是否支持數組?
zedGraph的PointPairList LineItem均支持數組,你可以這樣用
PointPairList[] ppl= new PointPairList [xrowCount]
LineItem[] line= new LineItem[xrowCount]
for (int j = 2; j < yColumnCount; j++)
{
x[j - 2] = (double)new XDate(DateTime.Parse(Convert.ToDateTime(gdvAnalysisData.HeaderRow.Cells[j].Text).ToShortDateString()));
//xi = gdvAnalysisData.HeaderRow.Cells[j].Text.ToString();
}
一開始我用的時二維數組,寫法如下:
for (int i = 0; i < xRowCount; i++)
{
double[,] storage = new double[xRowCount,yColumnCount - 2]; //定義二維數組
for (int k = 2; k < yColumnCount; k++)
{
storage[i,k - 2] = Convert.ToDouble(gdvAnalysisData.Rows[i].Cells[k].Text.ToString()); //將單元格中的數據填充到二維數組中去 K-2是把前兩列不是數據的列值去掉
Response.Write("x["+(k-2).ToString()+"]="+x[k-2].ToString()+" stroage["+i.ToString()+","+(k-2).ToString()+"]="+storage[i,k - 2].ToString() + "<br>"); //打印值檢測數據
}
這段代碼所獲得的結果是KO的,就是把gridview的單元格的數據完整地填充到我的二維數組中去,可這zedgraph仍然不能通過其來繪製曲線圖。所以,尋思了一段時間,我不得不重新用回交互數組來實現了。因爲交互數組就是數組的數組,數組中可以包含一個數組,這樣我的值就可以直接給zedGraph使用了。
交互數組的實現:
for (int i = 0; i < xRowCount; i++)
{
double[][] storage = new double[xRowCount][]; //定義交互數組
storage[i] = new double[yColumnCount - 2]; //定義交互數組的數組的長度
for (int k = 2; k < yColumnCount; k++) //對gridview中的列進行for循環
{
storage[i][k - 2] = Convert.ToDouble(gdvAnalysisData.Rows[i].Cells[k].Text.ToString()); 將單元格的值填充到交互數據中所對應的I的數組中
// Response.Write("x[" + (k - 2).ToString() + "]=" + x[k - 2].ToString() + " storage[" + i.ToString() + "][" + (k - 2).ToString() + "]=" + storage[i][k - 2].ToString()+"<br>"); 打出數據來進行較正
}
myCurve[i] = myPane.AddCurve(objectID[i].ToString(), x, storage[i], Color.FromArgb(ra.Next(1, 255), ra.Next(1, 255), ra.Next(1, 255)), SymbolType.Circle); //將X和stroage[i]這兩個數組的值填充到zedGraph中去,注意X軸是不變的,x軸的值就是時間段,也就是gridview中的列名
myCurve[i].Line.IsAntiAlias = true;
myCurve[i].Symbol.Fill = new Fill(Color.White);
myCurve[i].Symbol.Size = 7;
}
由上面的這段值就可以得到我想要的效果了,zedGraph不定曲線數不定列名的曲線就可以整出來了.
曲線實現的代碼;
private void OnRenderGraph(ZedGraphWeb zgw, Graphics g, MasterPane masterPane)
{
//圖表相關屬性設置
GraphPane myPane = masterPane[0];
// Set the title and axis labels
myPane.Title.Text = "總庫存比較曲線圖";
myPane.XAxis.Title.Text = "年-月-日";
myPane.YAxis.Title.Text = "庫存量";
myPane.YAxis.Scale.MinAuto = true;
myPane.YAxis.Scale.MaxAuto = true;
myPane.YAxis.Scale.MajorStepAuto = true;
myPane.YAxis.Scale.IsUseTenPower = false;
//獲取統計日期
DateTime beginDate = Convert.ToDateTime(txtAgentStorageBeginDate.Text.Trim());
DateTime endDate = Convert.ToDateTime(txtAgentStorageEndDate.Text.Trim());
//獲取供應商ID數組
string ObjectIDList = PSS.COMMON.Encrypt.Decode(Request.QueryString["AID"].ToString().Trim());
string[] agentID = ObjectIDList.Split(',');
int agentNum = agentID.Length; //要比較的供應商數量
myPane.XAxis.Type = AxisType.DateAsOrdinal;
myPane.XAxis.Scale.Format = "yyyy-MM-dd";
LineItem[] myCurve = new LineItem[agentNum];
//獲取GridView的列數,也就是要循環的數
PSS.BLL.Sys.Statistical bllStatistic = new PSS.BLL.Sys.Statistical();
int yColumnCount = bllStatistic.SysSumStoragesCompareList(ObjectIDList, beginDate, endDate).FieldCount; //列數
int xRowCount = gdvObjectStorage.Rows.Count; //行數
Random ra = new Random(1000);
//x軸
double[] x = new double[yColumnCount - 2];
for (int j = 2; j < yColumnCount; j++)
{
x[j - 2] = (double)new XDate(DateTime.Parse(Convert.ToDateTime(gdvObjectStorage.HeaderRow.Cells[j].Text).ToShortDateString()));
//string xi = gdvObjectStorage.HeaderRow.Cells[j].Text.ToString();
}
for (int i = 0; i < xRowCount; i++)
{
double[][] storage = new double[xRowCount][];
storage[i] = new double[yColumnCount - 2];
for (int k = 2; k < yColumnCount; k++)
{
storage[i][k - 2] = Convert.ToDouble(gdvObjectStorage.Rows[i].Cells[k].Text.ToString());
// Response.Write("x[" + (k - 2).ToString() + "]=" + x[k - 2].ToString() + " storage[" + i.ToString() + "][" + (k - 2).ToString() + "]=" + storage[i][k - 2].ToString()+"<br>"); 打出數據來進行較正
}
myCurve[i] = myPane.AddCurve(gdvObjectStorage.Rows[i].Cells[1].Text.ToString(), x, storage[i], Color.FromArgb(ra.Next(1, 255), ra.Next(1, 255), ra.Next(1, 255)), SymbolType.Diamond);
myCurve[i].Line.IsAntiAlias = true;
myCurve[i].Symbol.Fill = new Fill(Color.White);
myCurve[i].Symbol.Size = 7;
}
}
我的體會:
1.整個數據是一個數據挖掘的過程,如果利用數據挖掘或(OLAP)進行數據組織,實現其來應該是個很容易的事情,這就是一個小小的BI應用了。看來BI的市場大前錢途啊。
2.做統計報表分析,基本功還是在SQL,SQL統計就那個幾個字段幾個數據,得到的效果卻可以千變萬化,可萬變不離其中,還是要掌握好SQL語句。
3.我所碰到的這個問題應該不是僅侷限於如何傳值到zedgraph裏去,我覺得如何獲取數據並按相應的規則去組織數據纔是最重要的,思路最重要。
項目過程中的一點小知識及經驗,與大家分享。感謝在此問題中提供幫助的同仁同事們,歡迎大家指教。
利用zedGraph實現動態數據動態曲線(不定曲線數)的生成
http://www.dezai.cn/blog/article.asp?id=240