SQL

[T-SQL] IIF 與 CASE WHEN … THEN … ELSE … END


有個案子現場是前面的人做的,因為距離關係,分了三區設置 4G 網路,把資料扔回 Server 。

但妙的是其中有兩區相連,比如說 A/B 區。當 A 區設備沒開,A 區的流量就要歸總到 B 區算。

本來 Server 只負責接收資料,針對這種情形,建一個分析規則表,等待現場回傳後計算。

本來是在 SQL Server 2016 上開發測試,所以 T-SQL 語法習慣就選用
https://docs.microsoft.com/zh-tw/sql/t-sql/functions/logical-functions-iif-transact-sql

IIF(bool, true_value, false_value)

但實際上要跑的資料庫是 SQL Server 2008 R2 ,沒想到 SQL Server 2008 R2 不支援 IIF 。

我記得以前微軟文件會寫相容層級,找半天都找不到,只好一版版翻,似乎是從 SQL Server 2012 以後開始支援。

只好退回舊版,改用
https://docs.microsoft.com/zh-tw/sql/t-sql/language-elements/case-transact-sql

CASE
WHEN bool1 THEN result1
WHEN bool2 THEN result2
ELSE result3
END

所以,比如說原先要用:
(IIF(Col1=Col2, ‘真’, ‘假’)) AS 判斷
改成
(CASE WHEN Col1=Col2 THEN ‘真’ ELSE ‘假’ END) AS 判斷

Categories: 資料庫, SQL, 工作點滴, 技術分享 | 標籤: | 發表留言

[SQL] 透過 Access 轉換資料庫架構


有個客戶要結束節能業務,把他們的節能服務轉交給我們公司,他們原先的雲端監測系統阿祖 (Azure) Web Server 與 SQL Server 都要關閉,要由我們的監測系統自己處理。

它們的系統是現場 IPC 沒有查詢展示介面,監測資料在現場暫存後,就上傳到雲端資料庫,所以雲端資料庫每分鐘一筆,主要的資料表架構如下,每個月一個資料表:

分區視別字串點位識別字串資料時間值(字串)原始值(字串)
長度100最大長度50DateTime長度50長度50

所以在上面這個資料表架構下,每個月大概七百萬筆資料,約佔用空間500MB。

看了對方資料架構我才知道,為何對方網頁限制每次查詢時間間隔不能超過七天,而且每次查詢起來大概要跑 3 分鐘才會出來。

我的架構分成兩部分,現場端有IPC安裝 SQL Server Express資料庫,每分鐘一筆,統計分析後上傳到雲端資料庫,不同時間間隔分開放,以小時、日、月、年為單位統計。現場資料庫考慮效能及容量,將點位抽出來列表,核心資料表則所有時間放在一起不分表,資料架構大概是:

資料時間點位1點位2點位n狀態
DateTimeDoubleDoubleDouble長度n

所以每月大概變為43200筆,約佔用空間 50 MB。

所以需要將資料格式進行轉換,將對方類似一維的數值表格架構轉成我的二維的數值表格架構。

首先先將對方的 Azure SQL Server 整個備份到我電腦的 SQL Server Developer,光是做這件事,我電腦硬碟就被吃掉 40 GB …

考慮到我電腦內的資料庫避免異動,確保乾淨,所以我不打算在 SQL Server 處理資料格式轉換,我打算把每個月的資料匯入到 Access 後再轉換成我要的格式。

Access 轉換的架構

查詢「刪除 data 資料」這個並不需要存在,是我開發過程期間測試 SQL 語法用的。

資料表 raw 是用來放置 SQL Server 的匯入暫存資料,資料表 data 則是樣本資料表,為了快速處理,我還是依照每個月複製貼上 Data_yyMM 資料表,進行格式轉換,最後再統一匯入一個資料表。

所以核心的程式碼很單純:

DoCmd.SetWarnings False ' 關閉告警視窗
DoCmd.RunSQL nowQuery ' 執行 SQL 語法
DoCmd.SetWarnings True ' 恢復告警視窗

所以上述部分迴圈的目的就是為了組合 SQL 語法來達成目的,所以後面說明就專注於 SQL 語法,而非 VBA 語法。

匯入資料到 raw

將 SQL Server 資料表資料匯入到 Access raw 資料表,SQL 語法如下:

INSERT INTO [raw]
SELECT 點位識別字串,資料時間,值
FROM [來源表格名] IN " [odbc; Driver=SQL Server Native Client 11.0; Server=(local); Database=資料庫; UID=帳號; PWD=密碼; ]

注意到了嗎?這邊最特別的就是 IN 子句,透過 IN 子句跨越資料庫,這功能只能在 OLEDB 資料庫驅動程式下用,所以表示 Access 內 VBA RunSQL 是跑 OLEDB 來做查詢,這部分說明沒寫,這段應該是這篇的精華。

對 In 子句延伸閱讀可在部落格右側搜尋輸入:In 子句。

插入時間

為了避免無效時間,所以先在 data 資料表內將時間插入,並可確保所有時間都不被遺漏,SQL 語法如下:

INSERT INTO [Data_yyMM]
SELECT DISTINCT 資料時間 FROM raw ORDER BY 資料時間

更新各欄資料

這邊就依據資料點位對應到資料欄位,逐欄更新,所以前一步插入時間很重要,插入時間後,就有 PK 欄可供 UPDATE 使用。逐欄迴圈 SQL 語法如下:

UPDATE [Data_yyMM]
INNER JOIN (
SELECT 資料時間, 值 FROM raw WHERE 點位識別字串="點位代碼"
) AS tabData ON ([Data_yyMM].rdate=tabData.資料時間)
SET [Data_yyMM].點位i = tabData.值

透過前一步的插入時間做為 PK 值 JOIN ,就可以整欄資料直接轉移。

刪除 Raw Data

這段 SQL 語法太簡單,應該無須解釋。

DELETE FROM raw

小結

所以就能快速透過 Access 執行巨集持續進行不同資料表的資料匯入、表格架構轉換,不過雖然有跑刪除 Raw Data ,但還是無法防止 Access 長大,因此長大到 2 GB 時, VBA 會被中斷,重新壓縮 Access 檔案,再改回圈開始處,就可以繼續往下跑。

我這時會先把 Data_yyMM 搬到其他 Access 檔案,把這個轉換用的 Access 盡量空出來跑資料轉換,不過我把 Data_yyMM 合併的 VBA 也寫再這個轉換檔,所以最後我會把所有 Data_yyMM 搬回來合併。

Categories: 資料庫, SQL, 工作點滴, 技術分享 | 標籤: | 發表留言

[SQL Server] 資料庫損毀與修復


週日傍晚異常斷電,UPS 也掛了,周一早上重開後,伺服器一直跑得不順,在事件檢視器發現資訊訊息,建議跑 DBCC CHECKDB (SQL Server 2014 的資料庫)

Info:825 建議進行資料庫檢查
Info:825 建議進行資料庫檢查

另外也有錯訊息

Fail:8646 建議進行資料庫檢查
Fail:8646 建議進行資料庫檢查
Fail:824 建議進行資料庫檢查
Fail:824 建議進行資料庫檢查

周一下午跑過
DBCC CHECKDB (db)

Info:8957 資料庫修復結果
Info:8957 資料庫修復結果

,修復了 77 個錯誤。

但此之後,就頻繁發生 LOCK 錯誤

Fail:1204 無法 LOCK 資源
Fail:1204 無法 LOCK 資源

並且仍然會發生建議要跑 DBCC 的錯誤。

Info:825 建議進行資料庫檢查
Info:825 建議進行資料庫檢查

考慮了一晚,周二早上決定建新資料庫:

  1. 新建 db1 ,把 db 周日凌晨定時備份還原到 db1
  2. db 更名為 db_o ,db1 更名為 db
  3. 用匯入匯出精靈把 周日備份後的資料從 db_o 匯入到 db1
  4. 所以 db_o 除了人為測試與自動備份外,等同無人存取

週二晚上本著廢物利用的精神,平常沒機會玩資料庫修復,跑:
DBCC CHECKDB (db_o)
多次,每次跑完,錯誤數都比前一次多 (主要為索引錯誤)

Info:8957 資料庫檢查52個錯誤
Info:8957 資料庫檢查52個錯誤
Info:8957 資料庫檢查54個錯誤
Info:8957 資料庫檢查54個錯誤


DBCC CHECKDB (db_o, REPAIR_REBUILD)

Info:8957 資料庫檢查59個錯誤
Info:8957 資料庫檢查59個錯誤

無法修復

另外發生資料庫存取時間過長。

Info:833 存取超過15秒
Info:833 存取超過15秒

改跑
DBCC CHECKDB (db_o, REPAIR_ALLOW_DATA_LOSS)

Info:8957 資料庫修復60個錯誤
Info:8957 資料庫修復60個錯誤

修復了 60 個錯誤,又比上面 DBCC CHECKDB (db_o) 檢查的錯誤要多。

再跑一次:
DBCC CHECKDB (db_o, REPAIR_ALLOW_DATA_LOSS)

Info:8957 資料庫修復11個錯誤
Info:8957 資料庫修復11個錯誤

又修復了 11 個錯誤。

早上再跑一次
DBCC CHECKDB (db_o, REPAIR_ALLOW_DATA_LOSS)

Info:8957 資料庫修復結果
Info:8957 資料庫修復結果

終於沒錯誤了。

周二傍晚 db (原db1) 在事件檢視器有跳出資訊建議跑 DBCC CHECKDB ,但今天改跳出故障,建議跑 DBCC CHECKDB ,回頭翻了跳電前的 db_o 是有資訊事件建議跑 DBCC CHECKDB ,所以我想應該是還原時,把原先 db_o 的索引錯誤還原到 db1 ,利用午休的時間跑
DBCC CHECKDB (db, REPAIR_ALLOW_DATA_LOSS)
多次,直到沒有錯誤為止。

沒錯誤後,另外跑重建索引
DBCC DBREINDEX (DataTable)

但是晚上仍然發生建議跑 DBCC CHECKDB ,我有跑了

Info:825 建議進行資料庫檢查
Info:825 建議進行資料庫檢查


DBCC CHECKDB(db)

Info:8957 資料庫檢查0個錯誤
Info:8957 資料庫檢查0個錯誤

顯示 0 個錯誤,此外上面 LOCK 錯誤訊息持續出現。

目前找不到問題來源,懷疑可能跳電時,硬碟或 RAID 控制卡有受損,因為有些問題是持續存取時間過久,這次事件就是純練功修復資料庫了。

Categories: SQL, 工作點滴, 技術分享 | 標籤: | 1 則迴響

[T-SQL] SQL認證帳號新增資料庫未正常掛入使用者


工作的資料庫其中一個資料表超過 2 億 2 千萬筆,但這個資料表常常用在 JOIN 上,導致工作日查詢網頁時,大概要拖到 20 秒以上,假日沒人用倒還好,大概 3 秒可以出來。

某個資料表超過兩億筆
某個資料表超過兩億筆

這個資料表存了超過兩百個專案的資料,所以考慮採專案切割的方式,將每個專案資料切到不同資料庫,再由各專案資料庫的資料表進行 JOIN ,減少維度障礙。

超過兩百個專案進行切割,換句話說就是要建立超過兩百個資料庫,所以打算建立完整的 T-SQL 語法,方便後續使用。

正常建立的資料庫使用者如下,考慮到管理問題,預設加入 [BUILTIN\Administrators] ,再加入自行建立的 SQL 認證的 [馬賽克B] ,以下簡稱 UserB 。

資料庫的使用者
資料庫的使用者

開發的時候用測試機來測 T-SQL ,使用本機 Windows 認證,所使用的 T-SQL 語法簡化如下:

USE [master]
GO
/* 建立資料庫 */
GO
USE [專案資料庫]
GO
/****** User [BUILTIN\Administrators] ******/
CREATE USER [BUILTIN\Administrators] FOR LOGIN [BUILTIN\Administrators] WITH DEFAULT_SCHEMA=[dbo]
GO
/* BUILTIN\Administrators Set db_owner */
/*
EXEC sp_addrolemember 'db_owner','BUILTIN\Administrators' /* Before MSSQL 2008r2 */
*/
ALTER ROLE [db_owner] ADD MEMBER [BUILTIN\Administrators]
GO
/* --分隔線-- */
/****** User [*******B] ******/
CREATE USER [*******B] FOR LOGIN [*******B] WITH DEFAULT_SCHEMA=[dbo]
GO
/* [*******B] Set db_owner */
ALTER ROLE [db_owner] ADD MEMBER [*******B]
GO


正常建立完成的 UserB 點選 [屬性] 可看到 [使用者名稱]、[登入名稱] 都如同 UserB 的內容,左下角的輸出畫面可以看到沒有錯誤。

正常建立的 UserB 帳號
正常建立的 UserB 帳號

測試完沒問題後,從我的電腦跑,變成遠端存取建立資料庫,但這一建立就發生錯誤。

遠端建立無法正常加入使用者
遠端建立無法正常加入使用者

起初發現是 UserB 無法加入這個資料庫,但用手動也加入不了,經詳細比對後,發現是 SQL Server 自動掛入的 dbo 帳號會變成:

[使用者名稱]=dbo,但 [登入名稱] =UserB ,然後 [登入名稱] 又不給我改回 dbo ,所以無法加入 UserB,只好砍掉重練。

經過多次測試,發現上面的 T-SQL 語法從分隔線處分成兩次執行,第一次執行時,SQL Server 會正常掛入 dbo 帳號,第二次執行就可以正常掛入 UserB 帳號。

目前要得到我要的資料庫安全性登入帳號的測試結論是:

  1. 本機使用 Windows 認證時,上面 T-SQL 語法可以同次執行
  2. 遠端使用 SQL Server 認證時,上面的 T-SQL 語法需要從分隔線處分兩次執行

原因我還沒找到,做個心得記錄,後面可以翻查。

本機 Windows 認證我測試過 SQL Server 2008r2、2012、2014、2016、2017 都正常。

Categories: SQL, 工作點滴, 技術分享 | 標籤: | 發表留言

在WordPress.com寫網誌.

%d 位部落客按了讚: