這邊跟大家介紹一套做 DB migration 的好東西 pt-online-schema-change
,這是可以讓 DBA 省時省心力的工具,他可以用簡單的指令讓修改資料表結構時不鎖表(可寫入)與接近零 downtime 的好物。
SQL
[C#][MSSQL] 連線結束後 Temp Table 沒有自動釋放 !?
上次 [C#] SQL 資料庫 Connection Pool 連線池觀念釐清 有提到可以開啟 Connection Pool 來減少開啟連線的效能耗損,但最近發現有 Stored Procedure 執行後沒有手動 Drop Temp Table,而 .NET 又將連線丟回 Pool 裡面造成 Temp Table 沒有被正常釋放的問題,下面來測試看看是不是真的會有這種情況發生。
[MSSQL] SQL Server 的一些眉眉角角 part.1
目前工作 Database 方面主要都在使用 SQL Server,寫了近兩年的 Stored Procedure 也遇到了許多的問題,在這裡簡單筆記一下。
- 暫存資料表小知識
*#
或##
建立的暫存資料表,可從tempdb.暫存資料表
找到。
*#
建立的暫存資料表只有該連線可以取用;##
則是全部連線均可使用。
* 連線結束時皆會自動刪除,若程式中有 Connection Pool 來管理連線則不會立刻刪除。
* 父層 Stored Procedure 建立的暫存資料表子層也可以使用;子層 Stored Procedure 建立的暫存資料表父層也可以使用 - 若開啟交易 (Transaction) 但無提交異動 (Commit) 會發生資料表鎖死 (Table Lock) 的問題,切記要提交異動 (Commit)。
- 使用主鍵來
UPDATE
為資料鎖 (Row Lock),若使用其他條件則有可能會升級成資料頁鎖(Page Lock) 或資料表鎖 (Table Lock)。 - 交易 (Transaction) 具有復原機制 (RollBack),但實際上資料表中的資料已經被異動且鎖定的關係所以無法取得,可利用
WITH(NOLOCK)
來取得被鎖定的髒資料。 - 未開啟交易時使用
CURSOR
或UPDATE FROM
若超過五千筆時可能會造成鎖定與死結,可使用ROW_NUMBER()
排序資料塞入暫存資料表並使用WHILE
來一筆一筆更新資料避開鎖定,速度雖慢但能減少鎖死。
123456789101112131415161718192021222324252627282930313233343536373839404142SET NOCOUNT ONDECLARE@RowNum INT,@RowCount INT,@Temp_Id INTSELECTROW_NUMBER() OVER(ORDER BY [Id]) AS RowNum,[Id],[Name]INTO#TempFROM[exfast].[dbo].[TableA] WITH(NOLOCK)SELECT@RowNum = 1,@RowCount = (SELECT SUM(1) FROM #Temp)WHILE(@RowNum <= @RowCount)BEGINSELECT@Temp_Id = [Id]FROM#TempWHERERowNum = @RowNumUPDATE[exfast].[dbo].[TableA]SET[Name] = 'dddd'WHERE[Id] = @Temp_IdSET @RowNum = @RowNum + 1ENDDROP TABLE #Temp - 高負載情況下使用
UPDATE OUTPUT
的方法來取得資料更新前後的狀態,可減少SELECT
次數提高效率。
123456789101112131415161718192021DECLARE @TempA TABLE([INSERTED_Id] INT,[INSERTED_Name] NVARCHAR(32),[DELETED_Id] INT,[DELETED_Name] NVARCHAR(32))UPDATE[exfast.Helper].[dbo].[TableA]SET[Name] = 'qqqq'OUTPUTINSERTED.[Id],INSERTED.[Name],DELETED.[Id],DELETED.[Name]INTO@TempASELECT * FROM @TempA - 高負載情況下可能會將隔離層級拉高到 Serializable,這時使用交易會發生相同資料表
SELECT
UPDATE
中的 Shared Lock 與 Exclusive Lock 撞車造成 DeadLock,可在SELECT
加上WITH(UPDLOCK)
來減少此問題。
1234567891011121314151617-- 故意指定隔離層級模擬撞車的情況SET TRANSACTION ISOLATION LEVEL SERIALIZABLEBEGIN TRANSELECT * FROM [exfast.Helper].[dbo].[TableA]WAITFOR DELAY '00:00:10'UPDATE[exfast.Helper].[dbo].[TableA]SET[Name] = 'cccc'WHERE[Id] = 2ROLLBACK
[Docker][Linux] SQL Server 安裝筆記
環境:Window 7
- 安裝 Docker Toolbox
- 新增 Volume
1 |
docker volume create vol-mssql |
- 建立容器
1 2 3 4 5 6 7 8 9 10 |
docker run \ --restart=always \ --name mssql \ --mount "source=vol-mssql,target=/var/opt/mssql" \ -e "ACCEPT_EULA=Y" \ -e "SA_PASSWORD=1OCHWiY9O#RF" \ -e "MSSQL_PID=Express" \ -e "MSSQL_COLLATION=Chinese_Taiwan_Stroke_CI_AS" \ -p 1433:1433 \ -d microsoft/mssql-server-linux:latest |
- 更新時區
1 2 3 4 |
docker exec -ti mssql bash apt-get update apt-get install tzdata -y dpkg-reconfigure tzdata |
參考資料:
[MSSQL] 開啟對外 TCP/IP Port 連線
- 電腦管理
- 服務與應用程式
- SQL Server 網路組態
- MSSQLSERVER
- TCP/IP
- 通訊協定
- 啟用:是
這樣就可以用預設 port 1433 連線囉
[MySQL][LeetCode][Medium] 177. Nth Highest Salary
心得
這題非常有趣,題目要求寫一預存程式輸入N並取出排名第N名的薪水為多少,這題與178. Rank Scores
差不多,所以可以直接套用。
題目
Write a SQL query to get the nth highest salary from the
Employee
table.
1234567 +----+--------+| Id | Salary |+----+--------+| 1 | 100 || 2 | 200 || 3 | 300 |+----+--------+For example, given the above Employee table, the nth highest salary where n = 2 is
200
. If there is no nth highest salary, then the query should returnnull
.
答案
- 這題我是拿
178. Rank Scores
的解法直接套上去取N
1234567891011121314CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INTBEGINRETURN (# Write your MySQL query statement below.SELECT a.SalaryFROM ( SELECT x.*FROM Employee AS xGROUP BY x.SalaryORDER BY x.Salary DESC) AS zJOIN (SELECT @row_number := 0) AS y) AS aWHERE a.Rank = N);END
- 這解法是由高至低排序了所有的薪水,再用
LIMIT來獲取
Nth(因為LIMIT是從0開始所以N必須先減一),非常易懂的解法。
12345678910111213CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INTBEGINDECLARE M INT;SET M = N - 1;RETURN (# Write your MySQL query statement below.SELECT SalaryFROM EmployeeGROUP BY SalaryORDER BY Salary DESCLIMIT M, 1);END
參考:
[MySQL][LeetCode][Medium] 184. Department Highest Salary
心得
題目要求找出每個部門薪水最高的員工,若同部門最高薪水有複數則一起顯示,這題我在JOIN
部門清單的時候先去把各部門最高薪資數字先撈了出來,這樣在關聯兩張表的時候就可以直接搜尋員工薪水是否等於最高薪資。
問題
The
Employee
table holds all employees. Every employee has an Id, a salary, and there is also a column for the department Id.
12345678 +----+-------+--------+--------------+| Id | Name | Salary | DepartmentId |+----+-------+--------+--------------+| 1 | Joe | 70000 | 1 || 2 | Henry | 80000 | 2 || 3 | Sam | 60000 | 2 || 4 | Max | 90000 | 1 |+----+-------+--------+--------------+The
Department
table holds all departments of the company.
123456 +----+----------+| Id | Name |+----+----------+| 1 | IT || 2 | Sales |+----+----------+Write a SQL query to find employees who have the highest salary in each of the departments. For the above tables, Max has the highest salary in the IT department and Henry has the highest salary in the Sales department.
123456 +------------+----------+--------+| Department | Employee | Salary |+------------+----------+--------+| IT | Max | 90000 || Sales | Henry | 80000 |+------------+----------+--------+
答案
JOIN
部門清單的時候先去把各部門最高薪資數字先撈了出來
1234567891011# Write your MySQL query statement belowSELECT b.Name AS Department,a.Name AS Employee,a.Salary AS SalaryFROM Employee AS aJOIN ( SELECT z.*, (SELECT MAX(y.Salary)FROM Employee AS yWHERE z.Id = y.DepartmentIdLIMIT 0, 1) AS MaxSalaryFROM Department AS z) AS bON a.DepartmentId = b.Id AND a.Salary = b.MaxSalary- (2017/01/16) 寫 185. Department Top Three Salaries 時後發現的方法
1234567891011# Write your MySQL query statement belowSELECT b.Name AS Department,a.Name AS Employee,a.Salary AS SalaryFROM Employee AS aJOIN Department AS bON a.DepartmentId = b.IdWHERE ( SELECT COUNT(DISTINCT(z.Salary))FROM Employee AS zWHERE a.DepartmentId = z.DepartmentIdAND a.Salary < z.Salary) = 0
[MySQL][LeetCode][Medium] 178. Rank Scores
心得
題目要求找出成績排名,如果分數相同的話則相同名次,MySQL不像MSSQL有ROW_NUMBER()
可以用,只好用個變數來存了。
問題
Write a SQL query to rank scores. If there is a tie between two scores, both should have the same ranking. Note that after a tie, the next ranking number should be the next consecutive integer value. In other words, there should be no “holes” between ranks.
12345678910 +----+-------+| Id | Score |+----+-------+| 1 | 3.50 || 2 | 3.65 || 3 | 4.00 || 4 | 3.85 || 5 | 4.00 || 6 | 3.65 |+----+-------+For example, given the above
Scores
table, your query should generate the following report (order by highest score):
12345678910 +-------+------+| Score | Rank |+-------+------+| 4.00 | 1 || 4.00 | 1 || 3.85 | 2 || 3.65 | 3 || 3.65 | 3 || 3.50 | 4 |+-------+------+
答案
1 2 3 4 5 6 7 8 9 10 11 |
# Write your MySQL query statement below SELECT a.Score, b.Rank FROM Scores AS a FROM (SELECT x.* FROM Scores AS x GROUP BY x.Score ORDER BY x.Score DESC) AS z JOIN (SELECT @row_number := 0) AS y) AS b ON a.Score = b.Score ORDER BY `b`.`Score` DESC |