2020年12月24日 星期四

[研究] ASP.NET、SQL Server、空字串、null 的恩怨情仇

[研究] ASP.NET、SQL Server、空字串、null 的恩怨情仇

2020-12-24

空字串 : 有個箱子,只是沒放東西。ASP.NET (C#) 是 "", MS-SQL Server 是 ''

null : 連箱子都沒有。

Undefined (未定義) : 是否為箱子都不知道。

****************************************

在測試網頁上開發的查詢功能時,


<asp:SqlDataSource ID="SqlDataSource_Production_sub_type" runat="server" ConnectionString="<%$ ConnectionStrings:pmsConnectionString %>"

        SelectCommand="SELECT N'*' AS [Production_sub_type], N'<全部>' AS [Production_sub_type_display],'0' AS Priority

UNION

SELECT distinct [Production_sub_type], [Production_sub_type] AS [Production_sub_type_display],'1' AS Priority  FROM [ProductionList] ORDER BY Priority "></asp:SqlDataSource>


次類別 (Production_sub_type):<asp:DropDownList ID="DropDownList_Production_sub_type" runat="server" DataSourceID="SqlDataSource_Production_sub_type" DataTextField="Production_sub_type_display" DataValueField="Production_sub_type"></asp:DropDownList><br />


因為 table 中某些 records 的該欄位是 '' (空字串),結果下拉選單選到該項時候,查不到,選到其他項,正常。


****************************************

測試

string queryString = SqlDataSource1.SelectCommand;

... (略)

queryString = queryString + queryString.Replace("@Production_sub_type", "N'" + DropDownList_Production_sub_type.SelectedValue + "'");

... (略)

把 queryString 內容丟到 SSMS ( Microsoft SQL Server Management Studio) 中執行是正常的。


****************************************

敝人盡量不用 null  (可能有人讚同,有人反對,有人無意見)


(1)以前上課的老師建議


(2)個人經歷,例如

單一 table 倒沒甚麼問題,如過是多個 table 做 join 做 count、加、減、總和 或某些運算,

敝人碰過 SSMS 上可以正常執行一道複雜的 SQL (沒有參數),

ASP.NET 的 GirdView 無法顯示的情況,就是因為產出的欄位有些數值、有些null 

(詳情不記得,或許可以用 SQL 語法 CASE WHEN 處理後,再給 GridView 就可以顯示)


(3) SQL 語法判斷欄位值一般用 fieldName = '某值'

就算那個值是空字串也是這樣寫,但若是 null,要寫 filedname is null,指令下法不一致


所以 ASP.NET 網頁下拉選單選擇沒有值的項目,DB 要考慮把 '' 和 null 項目都撈出

ASP.NET 中 SQL 語法可能要寫成

WHERE (fieldname=@DropDownListChoice or (@DropDownListChoice=N'' and fieldname is null))


SQL 語法變麻煩了,如果這個欄位是數個 table join 時 on 的條件使用,那複雜度....


或者該欄位 '' 和 null 意義不同,ASP.NET 下拉選單可能要替 null 另給個名稱,例如 "null",

但好死不死,某些 table 的某些欄位的部分欄位值中有稱為 "null" 的字串值 ( 不是 null )

( Web API / Web Service 呼叫別系統而取得,儲存下來的 )


(4)不想欄位中又是 null,又是 "" (空字串)


--------------------


不用 null,SQL Server 的 Database Schema - Table Schema 設計


SQL Server 的資料表(table)「設計」時新增一個欄位(field)時候,預設是「允許Null」,且預設值是 null

敝人習慣把「允許Null」勾選拿掉,

若「資料型態」為字串類的,敝人會把預設值設定為 ''

若「資料型態」為數值類的,敝人會把預設值設定為 ''

若「資料型態」為日期類的,敝人會把預設值設定為 getdate() 或某個日期

若「資料型態」為布林值類的 (bit),敝人會把預設值設定為 1 或 0


(註:bit 後來不太用,因為原本需求只是表示 "未完成" 和 "完成",

後來需求又變,要求增加一種或 N 種狀態 (ex : 填寫中、階段X、階段Y、、,

只好把 bit 換成 nvarchar、int或其他,不然就新增一個欄位來用,

然後修改所有受影響的諸多程式 )


--------------------


不用 null 後,ASP.NET (WebForm) 的 .aspx 困擾與設計


<asp:SqlDataSource ID="SqlDataSource1" ..(略)

        <InsertParameters>

            <asp:Parameter Name="Production_sub_type" Type="String" />

要改成下面,不然如果沒有輸入值,會自動把空字串 "" 轉成 null,因為敝人設定欄位不允許 null,就出錯了

            <asp:Parameter Name="Production_sub_type" Type="String" DefaultValue="" ConvertEmptyStringToNull="false" />


日期欄位

<asp:Parameter Name="CreateDateTime" Type="DateTime" />

改為

<asp:Parameter Name="CreateDateTime" Type="DateTime"  DefaultValue="<%: DateTime.Now%>" ConvertEmptyStringToNull="false" />

<asp:Parameter Name="CreateDateTime" Type="string"  DefaultValue='<%: DateTime.Now.ToString("yyyy-MM-dd")%>' ConvertEmptyStringToNull="false" />


另外就是 需求者 希望該筆資料沒有填寫或送出時,

時間欄位和數值欄位不要顯示值 (只好在 GirdView 的 RowDataBound 或 DataBound 或其他地方動手腳)


--------------------

不用 null 後,ASP.NET (WebForm) 的 .aspx.cs 困擾與設計


也是這次碰到的情況,想到可能是 "" 被轉成 null 所以查不到


SqlDataSource1.SelectCommand = @"

-- DECLARE  @Product_ancestor_name nvarchar(50)

-- SET @Product_ancestor_name=N''


SELECT * FROM [ProductList] 

WHERE ... (略)

AND (@Product_sub_type ='*' or Product_sub_type=@Product_sub_type)


改成下面


AND (@Product_sub_type ='*' or Product_sub_type=@Product_sub_type or ( Product_sub_type='' and @Product_sub_type is null ))


但是無效 (有寫錯?)


後來在

SqlDataSource1.SelectParameters.Add("Product_sub_type", DropDownList_Product_sub_type.SelectedValue );

下方增加一行

SqlDataSource1.SelectParameters["Product_sub_type"].ConvertEmptyStringToNull = false;

成功解決。

註:另一種方式是 DropDownList 的 SELECT 用 WHERE 條件排除掉 "",讓 DropDownList 沒得選 "",但似乎不完善。

****************************************

至於倒底怎樣最好,看情況,見人見智吧。

(完)


沒有留言:

張貼留言