## [範本] SQL 預存程序(stored procedure) + `ADO.Net`
### 1. 搜尋"平均"年齡(小數點後第一位)
#### 1-1 於 SQL 建立 GetAvgOfPeopleAge 預存程序
```sql=
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--
Create PROCEDURE GetAvgOfPeopleAge -- Your Procedure Name
-- 呼叫 Procedure 要送進來的參數
@City nvarchar(50)='',
@Status nvarchar(50)=''
AS
BEGIN
SET NOCOUNT ON;
-- 宣告與給予空白初始值 WHERE字串 變數
DECLARE @WhereClause NVARCHAR(MAX) = '';
-- 根據條件動態構建 WHERE 子句
IF @City <> ''
BEGIN
SET @WhereClause = @WhereClause + ' AND City = @City'; --最前面有留空白
END
IF @Status <> ''
BEGIN
SET @WhereClause = @WhereClause + ' AND Status = @Status'; --最前面有留空白
END
DECLARE @Query NVARCHAR(MAX) = '';
SET @Query = '
BEGIN
SELECT Round(AVG(FloatD),1) as AGE_Result
FROM (
SELECT Round((DATEDIFF(day,CY,getdate()) /365.0),1) as FloatD
FROM (
SELECT CASE
WHEN SUBSTRING(StartTime, 1, 1) IN (''0'', ''1'')
THEN CONCAT(
TRY_CAST(TRY_CAST(SUBSTRING(StartTime, 1, 3) AS int) + 1911 AS VARCHAR(4)),
''-'',
SUBSTRING(StartTime, 4, 2),
''-'',
SUBSTRING(StartTime, 6, 2)
)
ELSE NULL
END AS CY
FROM People
WHERE 1=1'+@WhereClause+'
) as PeopleCY
) as Result
END'
--EXEC sp_executesql @Query, N'@City NVARCHAR(50), @Status NVARCHAR(50)', @City, @Status;
EXEC sp_executesql @Query;
END;
```
- Note:如果要**測試預存程序**,可以在查詢視窗輸入:
```text=
EXEC GetAvgOfPeopleAge @City='01', @Status='在職';
```
#### 1-2 於 MVC5 中建立 GetAvgOfPeopleAge 函式
```csharp=
static string GetAvgOfPeopleAge(string connectionString, string city,string status)
{
string avgAge = "";
double ageNum = 0;
using (SqlConnection conn = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand("GetAvgOfPeopleAge", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@City", city ?? "");
cmd.Parameters.AddWithValue("@Status", status ?? "");
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.Read())
{
avgAge = reader[0].ToString();
if (Double.TryParse(avgAge, out ageNum))
{
ageNum = Math.Round(ageNum, 1);
//Console.WriteLine($"{avgAge}");
}
else
{
//Console.WriteLine($"無法轉型");
}
}
}
}
}
return ageNum.ToString();
}
```
#### 1-3 於 MVC5 Controller 內部呼叫中 GetAvgOfPeopleAge 函式
```csharp=
// GetAvgOfPeopleAge 函式參數由 Controller 參數抓取
try
{
string avgAge = GetAvgOfPeopleAge(ConnectionString, city, status);
if (avgAge == string.Empty)
{
//Console.WriteLine($"The average is null.");
}
else
{
//Console.WriteLine($"The average age in {city} is {avgAge}.");
}
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
```
---
### 2. 搜尋"最年長"、"最年輕"的人(相同的都要取出)
#### 2-1 於 SQL 建立 GetAgingListLoop 預存程序
```sql=
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--
Create PROCEDURE GetAgingListLoop -- Your Procedure Name
-- 呼叫 Procedure 要送進來的參數
@City NVARCHAR(50)='',
@Status NVARCHAR(50)='',
@OrderInput NVARCHAR(50)='',
@OrderWay NVARCHAR(50)='',
@RowNumber NVARCHAR(50)=''
AS
BEGIN
SET NOCOUNT ON;
--動態組 Where 查詢字串
DECLARE @WhereClause NVARCHAR(MAX) = '';
IF(@City!='')
BEGIN
SET @WhereClause=@WhereClause+'AND City=@City '; -- 最後面要有空格
END
IF(@Status!='')
BEGIN
SET @WhereClause=@WhereClause+'AND Status=@Status ';
END
-- 判斷 Input 為何,變動 @OrderWay 變數
IF(@OrderInput='Old')
BEGIN
SET @OrderWay='DESC';
END
ELSE IF(@OrderInput='Young')
BEGIN
SET @OrderWay='ASC';
END
-- 組查詢字串
DECLARE @Query NVARCHAR(MAX) = ''
SET @Query='
WITH CTE AS (
SELECT [Name], [Status], [StartTime], [City],
ROW_NUMBER() OVER (ORDER BY StartTime '+@OrderWay+') AS RowNumber
FROM [testdb].[dbo].[People]
WHERE 1=1 '+ @WhereClause+'
)
SELECT Name,Status,City,
Round((DATEDIFF(day,CY,getdate()) /365.0),1) as FloatD
FROM(
SELECT Name, Status, City,
CASE
WHEN SUBSTRING(StartTime, 1, 1) IN (''0'', ''1'')
THEN CONCAT(
TRY_CAST(TRY_CAST(SUBSTRING(StartTime, 1, 3) AS int) + 1911 AS VARCHAR(4)),
''-'',
SUBSTRING(StartTime, 4, 2),
''-'',
SUBSTRING(StartTime, 6, 2)
)
ELSE NULL
END AS CY
FROM CTE
WHERE RowNumber='+@RowNumber +'
) AS FIRSTQuery
'
-- 執行 SQL字串指令(可以額外輸入參數化參數)
--EXEC sp_executesql @Query,N'@City NVARCHAR(50),@Status NVARCHAR(50),@OrderInput NVARCHAR(50),@RowNumber NVARCHAR(50)',@City,@Status,@OrderInput,@RowNumber
EXEC sp_executesql @Query;
END
```
- Note:如果要**測試預存程序**,可以在查詢視窗輸入:
```text=
EXEC GetAgingListLoop @City='01', @Status='在職', @OrderInput='Old', @RowNumber='1';
```
#### 2-2 於 MVC5 中建立 GetAgingPeopleLoop 函式
```csharp=
static List<People> GetAgingPeopleLoop(string connectionString, string city, string status,string orderInput) // string rowNumber 可以不用,內部會宣告,並動態遞增
{
List<People> PeopleList = new List<People>();
int start_Row_number = 1;
string Aging_number = ""; // 最為 年輕 或 年長 的歲數
try
{
while (true)
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand("GetAgingListLoop", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@City", city ?? "");
cmd.Parameters.AddWithValue("@Status", status ?? "");
cmd.Parameters.AddWithValue("@OrderInput", orderInput ?? "");
cmd.Parameters.AddWithValue("@RowNumber", start_Row_number.ToString() ?? "");
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
if (reader.Read())
{
if (start_Row_number == 1)
{
People people = new People();
people.Name = reader["Name"].ToString();
people.Age = reader["FloatD"].ToString();
people.City = reader["City"].ToString();
people.Status = reader["Status"].ToString();
PeopleList.Add(people);
Aging_number = reader["FloatD"].ToString();
start_Row_number += 1;
}
else
{
if (reader["FloatD"].ToString() != Aging_number)
{
break;
}
else
{
People people = new People();
people.Name = reader["Name"].ToString();
people.Age = reader["FloatD"].ToString();
people.City = reader["City"].ToString();
people.Status = reader["Status"].ToString();
PeopleList.Add(people);
start_Row_number += 1;
}
}
}
}
else
{
break;
}
}
}
}
}
return PeopleList;
}
catch (Exception ex)
{
//Console.WriteLine(ex.Message);
return PeopleList;
}
}
```
#### 2-3 於 MVC5 Controller 內部呼叫中 GetAgingPeopleLoop 函式
```csharp=
// GetAgingPeopleLoop 函式參數由 Controller 參數抓取
List<People> ListofPeople = GetAgingPeopleLoop(ConnectionString, city, status, orderInput);
if (ListofPeople.Count!=0)
{
Console.WriteLine($"The ListofPeople is not null.");
}
else
{
Console.WriteLine($"The ListofPeople is null");
}
```
#### X-1 於 SQL 建立 GetAgingList 預存程序(參考,有錯誤)
```sql=
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--
Create PROCEDURE GetAgingList -- Your Procedure Name
-- 呼叫 Procedure 要送進來的參數
@City nvarchar(50)='',
@Status nvarchar(50)='',
@OrderInput nvarchar(50)='',
@OrderWay nvarchar(50)=''
AS
BEGIN
SET NOCOUNT ON;
--動態組 Where 查詢字串
DECLARE @WhereClause NVARCHAR(MAX) = '';
IF(@City!='')
BEGIN
SET @WhereClause=@WhereClause+'AND City=@City '; -- 最後面要有空格
END
IF(@Status!='')
BEGIN
SET @WhereClause=@WhereClause+'AND Status=@Status ';
END
-- 判斷 Input 為何,變動 @OrderWay 變數
IF(@OrderInput='Old')
BEGIN
SET @OrderWay='DESC';
END
ELSE IF(@OrderInput='Young')
BEGIN
SET @OrderWay='ASC';
END
-- 組查詢字串
DECLARE @Query NVARCHAR(MAX) = ''
SET @Query='
BEGIN
SELECT Round(AVG(FloatD),1) as AGE_Result
FROM (
SELECT TOP 1 WITH TIES Name,Status,City,Round((DATEDIFF(day,CY,getdate()) /365.0),1) as FloatD
FROM (
SELECT
Name,
Status,
City,
CASE
WHEN SUBSTRING(StartTime, 1, 1) IN (''0'', ''1'')
THEN CONCAT(
TRY_CAST(TRY_CAST(SUBSTRING(StartTime, 1, 3) AS int) + 1911 AS VARCHAR(4)),
''-'',
SUBSTRING(StartTime, 4, 2),
''-'',
SUBSTRING(StartTime, 6, 2)
)
ELSE NULL
END AS CY
FROM People
WHERE 1=1 '+@WhereClause+'
ORDER BY FloatD '+@OrderWay+'
) as PeopleCY
) as Result
END
'
-- 執行 SQL字串指令(可以額外輸入參數化參數)
--EXEC sp_executesql @Query,N'@City nvarchar(50),@Status nvarchar(50),@OrderWay nvarchar(50)', @City, @Status,@OrderWay
EXEC sp_executesql @Query;
END
```