Tìm đến thư mục C:\Users\USERNAME\AppData\Local\Atlassian\SourceTree
Xóa hoặc đổi tên file passwd.
Thoát ra SourceTree sau đó mở lại, thực hiện các action như Pull, Push, SourceTree sẽ yêu cầu nhập lại username và password. Khi đó sẽ update được.
Nguồn tham khảo:
https://stackoverflow.com/questions/45821037/how-do-i-set-my-password-in-sourcetree
Ngoài ra còn 1 cách nữa là vào Tools -> Options -> tab Authentication, xóa hết các tài khoản Git Saved Password.
Sau đó Pull lại sẽ hỏi user và pass. Nhập vào là xong
Thursday, December 12, 2019
Host Web Core trên WindowsService
Tạo 1 project web hoặc web API Core.
class Program sửa thành
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().RunAsService();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseContentRoot(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName))
.UseStartup<Startup>().
UseUrls("http://localhost:5000");
}
Mở file project, sửa lại giống như sau sau
class Program sửa thành
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().RunAsService();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseContentRoot(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName))
.UseStartup<Startup>().
UseUrls("http://localhost:5000");
}
Mở file project, sửa lại giống như sau sau
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeIdentifiers>win10-x64;</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
</PropertyGroup>
Chuyển vào thư mục project, chạy lệnh sau:
dotnet publish --configuration Release --self-contained -r win10-x64
Mở cmd lần nữa để chạy lệnh cài đặt service
sc create WindowsServiceHosted binPath= "C:\WindowsServiceHosted\bin\Debug\netcoreapp2.1\win7-x64\publish\WindowsServiceHosted.exe"
Chạy sc start WindowsServiceHosted để start service.
Chạy sc description WindowsServiceHosted "This is the description of the service.." để gán mô tả cho service
Chạy sc description WindowsServiceHosted "This is the description of the service.." để gán mô tả cho service
Gõ http://localhost:5000 để chạy web
Thursday, June 06, 2019
Sửa lỗi tạo project bị Access Denied
Tắt VS. Tìm file privateregistry.bin trong ổ C. Xóa đi rồi khởi động lại VS
Monday, January 14, 2019
Cách tạo link custom Uri scheme để chạy app như skype
Tạo Uri custom bằng cách đăng ký registry
private static void WriteRegistry()
{
try
{
using (var key = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\" + UriScheme))
{
string applicationLocation = Path.Combine(System.Windows.Forms.Application.StartupPath, "TestApp.exe");
key.SetValue("", "URL:" + FriendlyName);
key.SetValue("URL Protocol", "");
using (var defaultIcon = key.CreateSubKey("DefaultIcon"))
{
defaultIcon.SetValue("", applicationLocation + ",1");
}
using (var commandKey = key.CreateSubKey(@"shell\open\command"))
{
commandKey.SetValue("", "\"" + applicationLocation + "\" \"%1\"");
}
}
}
catch (Exception objEx)
{
ExceptionManger.WriteLog(objEx, "Exception happended in WriteRegistry.");
}
}
Như vậy khi mở trình duyệt gõ testapp: thì TestApp.exe sẽ chạy.
Muốn truyền thêm tham số cho TestApp thì hàm main cần nhận tham số string[] args. Sau đó xử lý tham số truyền vào trong mảng args.
private static void WriteRegistry()
{
try
{
using (var key = Registry.CurrentUser.CreateSubKey("SOFTWARE\\Classes\\" + UriScheme))
{
string applicationLocation = Path.Combine(System.Windows.Forms.Application.StartupPath, "TestApp.exe");
key.SetValue("", "URL:" + FriendlyName);
key.SetValue("URL Protocol", "");
using (var defaultIcon = key.CreateSubKey("DefaultIcon"))
{
defaultIcon.SetValue("", applicationLocation + ",1");
}
using (var commandKey = key.CreateSubKey(@"shell\open\command"))
{
commandKey.SetValue("", "\"" + applicationLocation + "\" \"%1\"");
}
}
}
catch (Exception objEx)
{
ExceptionManger.WriteLog(objEx, "Exception happended in WriteRegistry.");
}
}
Như vậy khi mở trình duyệt gõ testapp: thì TestApp.exe sẽ chạy.
Muốn truyền thêm tham số cho TestApp thì hàm main cần nhận tham số string[] args. Sau đó xử lý tham số truyền vào trong mảng args.
Monday, November 19, 2018
Join 2 bảng chính, phụ nhưng chỉ trả về 1 dòng đơn nhất cho mỗi dòng bảng chính
Sử dụng Cross Apply
https://stackoverflow.com/questions/2043259/sql-server-how-to-join-to-first-row?rq=1
https://stackoverflow.com/questions/2043259/sql-server-how-to-join-to-first-row?rq=1
Friday, October 05, 2018
Switch In Và Switch Out Với Table Partitioning
Bài viết tham khảo tại: http://www.sqlviet.com/blog/switch-in-va-switch-out-voi-table-partitioning
Một trong những ưu điểm của phân đoạn bảng là bạn có thể dễ dàng loại bỏ một lượng lớn bản ghi ra khỏi bảng, cũng như dễ dàng nhập một lượng lớn bản ghi vào bảng, thay vì phải sử dụng các lệnh INSERT và DELETE.
Ví dụ, bạn có bảng BanHang chứa dữ liệu bán hàng qua nhiều năm. Nay nhận thấy dữ liệu của năm 2010 không còn cần dùng đến thường xuyên, bạn muốn chuyển dữ liệu này sang một bảng khác để giảm nhẹ bảng chính. Nếu không có partition, bạn phải dùng hai lệnh: INSERT vào bảng mới và DELETE trên bảng chính với điều kiện năm = 2010. Hai lệnh này đều rất tốn kém vì mỗi bản ghi được thêm/xóa đều được ghi vào transaction log. Khi bảng được phân đoạn theo năm, bạn có thể dễ dàng chuyển đoạn cho 2010 sang bảng mới. Thao tác này gọi là switch out. Vì đây là thao tác DDL, dữ liệu sẽ được di chuyển tức thì.
Một ví dụ khác, bạn làm việc ở trung tâm dữ liệu của một hội sở ngân hàng, tập hợp dữ liệu của tất cả các chi nhánh. Mỗi cuối tháng, bạn tiếp nhận dữ liệu giao dịch từ các chi nhánh và gộp chung vào một bảng giao dịch chính. Nếu không có partition, bạn cần chạy lệnh INSERT cho từng chi nhánh. Khỏi cần bàn thêm cũng thấy việc này rất tốn thời gian. Tuy nhiên, nếu bạn phân đoạn bảng chính theo chi nhánh (ví dụ phân đoạn theo mã chi nhánh), bạn có thể nạp dữ liệu của từng chi nhánh vào bảng chính thông qua thao tác switch in. Cũng giống như switch out, đây là thao tác DDL nên cần rất ít tài nguyên hệ thống và dữ liệu được nạp ngay tức thì.
Switch out
Tôi dùng lại ví dụ trong bài Table Partitioning – Các Khái Niệm Cơ Bản, và tạo một bảng phân đoạn theo giá trị của cột id, đoạn 1: id nhỏ hơn 1000; đoạn 2: id từ 1001 – 2000; đoạn 3: id từ 2001 – 3000; và đoạn 4: id từ 3001 trở lên.
CREATE DATABASE MyPartDB GO -- tạo filegroup ALTER DATABASE MyPartDB ADD FILEGROUP FG1 ALTER DATABASE MyPartDB ADD FILEGROUP FG2 ALTER DATABASE MyPartDB ADD FILEGROUP FG3 ALTER DATABASE MyPartDB ADD FILEGROUP FG4 -- thêm data file vào mỗi filegroup ALTER DATABASE MyPartDB ADD FILE (NAME = N'F1', FILENAME = N'D:\DATA\MyPartDB_F1.ndf') TO FILEGROUP FG1 ALTER DATABASE MyPartDB ADD FILE (NAME = N'F2', FILENAME = N'D:\DATA\MyPartDB_F2.ndf') TO FILEGROUP FG2 ALTER DATABASE MyPartDB ADD FILE (NAME = N'F3', FILENAME = N'D:\DATA\MyPartDB_F3.ndf') TO FILEGROUP FG3 ALTER DATABASE MyPartDB ADD FILE (NAME = N'F4', FILENAME = N'D:\DATA\MyPartDB_F4.ndf') TO FILEGROUP FG4 GO USE MyPartDB GO -- tạo partition function CREATE PARTITION FUNCTION MyPartFunc_1(INT) AS RANGE LEFT FOR VALUES (1000, 2000, 3000) -- tạo partition scheme CREATE PARTITION SCHEME MyPartScheme_1 AS PARTITION MyPartFunc_1 TO (FG1, FG2, FG3, FG4) -- tạo bảng CREATE TABLE dbo.MyTablePart( MyID INT IDENTITY PRIMARY KEY, MyData VARCHAR(100) ) ON MyPartScheme_1(MyID) --thêm dữ liệu vào bảng INSERT INTO dbo.MyPartTable(MyData) SELECT DISTINCT a.name FROM master.SYS.syscolumns a
Nay tôi muốn chuyển toàn bộ dữ liệu của đoạn 1 sang một bảng khác. Tôi tạo một bảng mới và switch out đoạn 1 sang đó:
-- tạo bảng tạm CREATE TABLE dbo.Tbl_1000 ( MyID INT IDENTITY PRIMARY KEY, MyData VARCHAR(100)) ON FG1 GO --Switch out. Boom! ALTER TABLE dbo.MyPartTable SWITCH PARTITION 1 TO dbo.Tbl_1000 -- kiểm tra kết quả SELECT * FROM dbo.MyPartTable SELECT * FROM dbo.Tbl_1000
Vậy là dữ liệu đã được chuyển sang bảng Tbl_1000. Ở đây có vài điểm cần lưu ý:
- Bảng tạm phải có cấu trúc giống hệt với bảng chính.
- Bảng tạm phải được tạo trên cùng filegroup với đoạn mà bạn sắp chuyển sang. Ở ví dụ trên tôi cần chuyển đoạn 1 và tôi biết đoạn này nằm trên filegroup FG1 (vì sao tôi lại biết điều này?), nên tôi cũng cần tạo bảng tạm trên filegroup FG1.
- Và tuy không thể hiện trong ví dụ trên, bảng tạm phải trống trước khi switch out. Khi bảng tạm có chứa dữ liệu, SQL Server sẽ báo lỗi chứ không xóa dọn dữ liệu.
- Một điểm nữa là các nonclustered index trên bảng chính (MyPartTable) cũng phải được phân đoạn cùng với bảng. Khi đó index được gọi là aligned. Tôi sẽ viết kỹ hơn về aligned index trong một bài tới, tuy nhiên để bạn hình dung bạn hãy chèn lệnh sau đây vào ngay sau lệnh tạo bảng Table_1000:
CREATE UNIQUE INDEX ui_Tbl_1000 ON dbo.Tbl_1000(MyData)
Lệnh trên tạo một non-aligned index, và SQL Server sẽ từ chối thực hiện switch out và trả về một thông báo lỗi.
Switch in
Giả sử giờ tôi muốn chuyển lại dữ liệu trong bảng Tbl_1000 vào đoạn 1 của bảng MyPartTable. Các điều kiện áp dụng đối với switch out cũng vẫn cần được thỏa mãn (đồng thời đoạn cần được chuyển vào phải trống). Ngoài ra switch in còn đòi hỏi một điều kiện nữa, đó là cột trên bảng tạm tương ứng với partition key phải có chứa ràng buộc check theo đó nó chỉ cho phép chứa dải giá trị trùng khớp với đoạn tương ứng. Vì tôi muốn chuyển dữ liệu vào đoạn 1, và tôi biết đoạn 1 chỉ chứa các giá trị <= 1000, nên tôi cần tạo một ràng buộc check tương ứng trên bảng tạm rồi mới có thể switch in:
-- tạo ràng buộc ALTER TABLE dbo.Tbl_1000 ADD CONSTRAINT ck_id CHECK(MyID <= 1000) -- Switch in. Boom! ALTER TABLE Tbl_1000 SWITCH TO MyPartTable PARTITION 1
Phiên bản áp dụng: SQL Server 2005 trở lên
Table Partitioning – Split và Merge
Bài viết tham khảo tại: http://www.sqlviet.com/blog/table-partitioning-split-va-merge
Khi định nghĩa partition function bạn phải chỉ rõ giá trị biên của từng đoạn và theo đó, số lượng các đoạn cũng được xác định. Qua thời gian sẽ đến lúc bạn cần định nghĩa thêm các đoạn mới cho dữ liệu được thêm vào sau này. Ví dụ bạn phân đoạn bảng bán hàng theo năm của ngày giao dịch, tại thời điểm ban đầu bạn chia bảng làm 3 đoạn cho các năm 2009 trở về trước, 2010, và 2011 trở đi. Tại một thời điểm nào đó trước năm 2012 bạn cần tạo thêm đoạn mới cho năm 2012, và tương tự sau này cho 2013… Thao tác này gọi là split (chia đoạn).
Ngược lại, bạn cũng có thể muốn hợp các đoạn chứa dữ liệu lại với nhau để giảm bớt số lượng đoạn cần quản lý, hoặc đáp ứng yêu cầu thay đổi về nghiệp vụ (ví dụ Hà Tây nhập vào Hà nội). Với bảng bán hàng ở trên, có thể bạn muốn hợp hai đoạn 2009 và 2010 để gom tất cả dữ liệu cũ từ năm 2010 trở về trước vào một đoạn. Thao tác này gọi là merge (hợp đoạn). Lưu ý là partition merge khác với lệnh MERGE được đưa vào từ bản 2008.
Setup
Tôi dùng lại database trong bài Switch In Và Switch Out Với Table Partitioning và tạo thêm các đối tượng mới:
Ngược lại, bạn cũng có thể muốn hợp các đoạn chứa dữ liệu lại với nhau để giảm bớt số lượng đoạn cần quản lý, hoặc đáp ứng yêu cầu thay đổi về nghiệp vụ (ví dụ Hà Tây nhập vào Hà nội). Với bảng bán hàng ở trên, có thể bạn muốn hợp hai đoạn 2009 và 2010 để gom tất cả dữ liệu cũ từ năm 2010 trở về trước vào một đoạn. Thao tác này gọi là merge (hợp đoạn). Lưu ý là partition merge khác với lệnh MERGE được đưa vào từ bản 2008.
Setup
Tôi dùng lại database trong bài Switch In Và Switch Out Với Table Partitioning và tạo thêm các đối tượng mới:
USE MyPartDB GO CREATE PARTITION FUNCTION MyPartFuncD(DATETIME) AS RANGE RIGHT FOR VALUES ('2009-01-01', '2010-01-01', '2011-01-01') GO CREATE PARTITION SCHEME MyPartSchemeD AS PARTITION MyPartFuncD TO (FG1, FG2, FG3, FG4) GO CREATE TABLE dbo.BanHang( BanHang_ID INT IDENTITY, Ngay DATETIME, MaSP VARCHAR(10), SoLuong INT, ThanhTien INT, CONSTRAINT PK_BanHang PRIMARY KEY (BanHang_ID,Ngay) ) ON MyPartSchemeD(Ngay) GO --thêm dữ liệu vào bảng INSERT INTO dbo.BanHang(Ngay,MaSP,SoLuong,ThanhTien) SELECT '2008-03-25 19:26:12','100',2,500000 UNION ALL SELECT '2009-06-12 07:14:27','200',1,300000 UNION ALL SELECT '2010-01-22 10:01:44','300',3,600000 UNION ALL SELECT '2011-10-26 12:06:42','400',1,1000000 UNION ALL SELECT '2012-02-17 15:32:16','500',5,400000 GO --kiểm tra dữ liệu lưu vào các đoạn SELECT OBJECT_ID, index_id, partition_number, ROWS FROM SYS.partitions WHERE OBJECT_NAME(OBJECT_ID)='BanHang'
------------------------------ object_id partition_number rows 229575856 1 1 229575856 2 1 229575856 3 1 229575856 4 2
Như vậy bảng được lưu trữ đúng như mong đợi: bản ghi 2008 vào đoạn 1, bản ghi 2009 vào đoạn 2, bản ghi 2010 vào đoạn 3, còn hai bản ghi 2011 và 2012 vào đoạn 4. Số bản ghi của mỗi đoạn (cột “rows”) phản ánh điều này.
Split
Nay bạn muốn tạo thêm một đoạn cho 2012, bạn cần làm theo các bước như sau:
Nay bạn muốn tạo thêm một đoạn cho 2012, bạn cần làm theo các bước như sau:
--bước 1: tạo thêm 1 filegroup mới để chứa đoạn sắp được tạo ALTER DATABASE MyPartDB ADD FILEGROUP FG5 GO ALTER DATABASE MyPartDB ADD FILE (NAME = N'F5', FILENAME = N'D:\DATA\MyPartDB_F5.ndf') TO FILEGROUP FG5 GO --bước 2: mở rộng thêm partition scheme ALTER PARTITION SCHEME MyPartSchemeD NEXT USED FG5 GO --bước 3: định nghĩa thêm giá trị biên trong partition function ALTER PARTITION FUNCTION MyPartFuncD() SPLIT RANGE ('2012-01-01') GO --Thêm một số bản ghi mới và kiểm tra dữ liệu lưu vào các đoạn INSERT INTO dbo.BanHang(Ngay,MaSP,SoLuong,ThanhTien) SELECT '2012-03-12 14:25:12','100',3,750000 UNION ALL SELECT '2012-06-21 07:24:12','200',1,300000 GO SELECT OBJECT_ID, index_id, partition_number, ROWS FROM SYS.partitions WHERE OBJECT_NAME(OBJECT_ID)='BanHang'
object_id partition_number rows 229575856 1 1 229575856 2 1 229575856 3 1 229575856 4 1 229575856 5 3
Lưu ý ở bước 2, bạn yêu cầu đoạn tiếp theo sẽ sử dụng filegroup FG5 qua mệnh đề “NEXT USED FG5″. Ở bước 3, bạn tạo thêm một giá trị biên trong partition function, theo đó miền từ 2011-01-01 đến ngày tận thế được cắt tại 2012-01-01. Do đó đoạn 4 giờ được điều chỉnh để chứa dải giá trị từ 2011-01-01 đến trước 2012-01-01, và đoạn mới chứa dải từ 2012-01-01 đến ngày tận thế.
Vậy là đoạn mới đã được tạo (partition number 5), bản ghi 2012 cũ được chuyển vào đây và hai bản ghi 2012 mới cũng được lưu vào đoạn này.
Vậy là đoạn mới đã được tạo (partition number 5), bản ghi 2012 cũ được chuyển vào đây và hai bản ghi 2012 mới cũng được lưu vào đoạn này.
Merge
Việc hợp đoạn đơn giản hơn, để hợp đoạn 2009 vào 2010 bạn chỉ cần thực hiện lệnh sau
Việc hợp đoạn đơn giản hơn, để hợp đoạn 2009 vào 2010 bạn chỉ cần thực hiện lệnh sau
ALTER PARTITION FUNCTION MyPartFuncD() merge RANGE ('2009-01-01')
Hiểu một cách đơn giản là xóa đi giá trị biên 2009-01-01 và do đó tất cả các giá trị từ trước đến 2010-01-01 được lưu chung vào một đoạn. Vậy đoạn nào được dùng? Trước khi hợp, đoạn 1 lưu vào filegroup FG1, đoạn 2 lưu vào filegroup FG2. Sau khi hợp, đoạn 1 và đoạn 2 được gộp chung vào thành đoạn 1. Vậy đoạn 1 mới bây giờ được lưu ở FG1 hay FG2? Xin nhường cho bạn tìm câu trả lời.
Lưu ý
Không giống như Switch in và Switch out vốn chỉ thay đổi metadata trong database, Split và Merge kéo theo sự di chuyển của dữ liệu. Các bản ghi sẽ được chuyển sang đoạn mới thích hợp với giá trị partition key của nó. Trong ví dụ split ở trên, bản ghi 2012 ban đầu ở đoạn 4 được chuyển sang đoạn 5; còn ở ví dụ merge hai đoạn 2009 và 2010, các bản ghi ở một đoạn được chuyển qua đoạn kia. Điều này có thể gây kéo dài thời gian thực hiện khi bảng có lượng bản ghi lớn.
Không giống như Switch in và Switch out vốn chỉ thay đổi metadata trong database, Split và Merge kéo theo sự di chuyển của dữ liệu. Các bản ghi sẽ được chuyển sang đoạn mới thích hợp với giá trị partition key của nó. Trong ví dụ split ở trên, bản ghi 2012 ban đầu ở đoạn 4 được chuyển sang đoạn 5; còn ở ví dụ merge hai đoạn 2009 và 2010, các bản ghi ở một đoạn được chuyển qua đoạn kia. Điều này có thể gây kéo dài thời gian thực hiện khi bảng có lượng bản ghi lớn.
Index Partitioning
Bài viết tham khảo tại: http://www.sqlviet.com/blog/index-partitioning

Đồng thời index cũng tự động được thêm trường partition key là NgayGiaoDich, mặc dù bạn chỉ tạo index trên trường MaSP:
Với một bảng đã được phân đoạn, mỗi index được tạo ra theo mặc định được phân đoạn theo giống như bảng. Ví dụ bảng được phân đoạn theo năm của ngày giao dịch thì index cũng được phân đoạn theo cách đó. Index khi đó được gọi là aligned với bảng. Tuy nhiên bạn cũng có thể không phân đoạn cho index và để nó nguyên một khối, hoặc phân đoạn theo cách khác. Khi đó index được gọi là non-aligned.
Bạn hãy hình dung bảng như một cái bánh tét, và index là sợi lạt buộc xung quanh. Khi phân đoạn, chiếc bánh được cắt thành từng lát. Nếu sợi lạt cũng được cắt theo và mỗi sợi con giờ buộc xung quanh từng lát bánh, sợi lạt được gọi là aligned với chiếc bánh. Nếu sợi lạt không được cắt và vẫn bao xung quanh cả chiếc bánh to, sợi lạt được gọi là non-aligned với chiếc bánh.
SQL Server luôn ưu tiên phân đoạn index để nó aligned với bảng và luôn cố gắng phân đoạn khi có thể, vì nó đem lại nhiều thuận lợi cho các tác vụ trên bảng. Tuy nhiên khi đi vào chi tiết từng loại index, cách cư xử này được thể hiện qua những sắc thái khác nhau, ta sẽ xem xét kỹ hơn ở dưới đây.
Non-clustered Index Khi bạn tạo nonclustered index mà không chỉ định filegroup, SQL Server sẽ mặc định tạo index trên partition scheme của bảng, nghĩa là phân đoạn index cùng với bảng:
Bạn hãy hình dung bảng như một cái bánh tét, và index là sợi lạt buộc xung quanh. Khi phân đoạn, chiếc bánh được cắt thành từng lát. Nếu sợi lạt cũng được cắt theo và mỗi sợi con giờ buộc xung quanh từng lát bánh, sợi lạt được gọi là aligned với chiếc bánh. Nếu sợi lạt không được cắt và vẫn bao xung quanh cả chiếc bánh to, sợi lạt được gọi là non-aligned với chiếc bánh.
SQL Server luôn ưu tiên phân đoạn index để nó aligned với bảng và luôn cố gắng phân đoạn khi có thể, vì nó đem lại nhiều thuận lợi cho các tác vụ trên bảng. Tuy nhiên khi đi vào chi tiết từng loại index, cách cư xử này được thể hiện qua những sắc thái khác nhau, ta sẽ xem xét kỹ hơn ở dưới đây.
Non-clustered Index Khi bạn tạo nonclustered index mà không chỉ định filegroup, SQL Server sẽ mặc định tạo index trên partition scheme của bảng, nghĩa là phân đoạn index cùng với bảng:
USE PartTest GO CREATE TABLE dbo.BanHang( BanHang_ID INT IDENTITY, NgayGiaoDich DATETIME, MaSP INT, SoLuong INT, ThanhTien INT ) ON PScheme_NGD(NgayGiaoDich) GO CREATE INDEX CI_BanHang_MaSP ON dbo.BanHang(MaSP) GO
Bạn sẽ thấy index cũng được phân đoạn bằng partition scheme PScheme_NGD:
--code kiểm tra xem index có được phân đoạn SELECT i.name AS IndexName, i.type_desc, ps.name AS PartitionName FROM SYS.indexes i JOIN SYS.partition_schemes ps ON ps.data_space_id = i.data_space_id WHERE i.name = 'IX_BanHang_MaSP'

Đồng thời index cũng tự động được thêm trường partition key là NgayGiaoDich, mặc dù bạn chỉ tạo index trên trường MaSP:
--code để liệt kê các cột chứa trong index SELECT ind.name AS Index_Name, col.name AS Column_Name FROM SYS.indexes ind INNER JOIN SYS.index_columns ic ON ind.OBJECT_ID = ic.OBJECT_ID and ind.index_id = ic.index_id INNER JOIN SYS.COLUMNS col ON ic.OBJECT_ID = col.OBJECT_ID and ic.column_id = col.column_id WHERE ind.name = 'IX_BanHang_MaSP'

Ở ví dụ ở trên, index là non-unique. Khi bạn tạo unique index, tình hình sẽ khác đi một chút. SQL Server không thể tự động thêm trường partition key vào index vì như thế sẽ làm thay đổi tính chất của index. Ví dụ khi bạn tạo unique index trên trường BangHang_ID, trường này chỉ được chứa các giá trị duy nhất trong toàn bảng. Nếu SQL Server thêm trường NgayGD vào index, index đó sẽ chỉ đảm bảo tính duy nhất cho cặp BanHang_ID+NgayGD và khi đó BanHang_ID vẫn có thể chứa các giá trị lặp lại miễn là khác NgayGD. Vì thế, thay vì tự động sửa index, SQL Server sẽ kiểm tra xem index có chứa trường partition key hay không, nếu không thì nó báo lỗi và không cho tạo index. Ví dụ, lệnh tạo index sau sẽ báo lỗi:
CREATE UNIQUE INDEX UI_BanHang_BanHang_ID ON dbo.BanHang(BanHang_ID)
Msg 1908, Level 16, State 1, Line 1
Column ‘NgayGiaoDich’ is partitioning column of the index ‘UI_BanHang_BanHang_ID’. Partition columns for a unique index must be a subset of the index key.
Column ‘NgayGiaoDich’ is partitioning column of the index ‘UI_BanHang_BanHang_ID’. Partition columns for a unique index must be a subset of the index key.
Để khắc phục, bạn cần chỉ định rõ filegroup cho index:
CREATE UNIQUE INDEX UI_BanHang_BanHang_ID ON dbo.BanHang(BanHang_ID) ON [PRIMARY]
Khi đó index được tạo ra trên PRIMARY filegroup thay vì trên partition scheme PScheme_NGD, nói cách khác là nó không được phân đoạn theo bảng.
Clustered Index Vì clustered index chính là bảng nên nó đương nhiên phải được phân đoạn cùng với bảng. Cũng giống như trên, nếu index là non-unique SQL Server sẽ tự động bổ sung partition key vào index và phân đoạn nó. Còn nếu index là unique, trường partition key bắt buộc phải có mặt trong index nếu không sẽ bị lỗi. Tuy nhiên lưu ý là khi bạn chỉ định filegroup cho index, toàn bộ bảng sẽ được kéo sang filegroup mới và bảng sẽ không còn được phân đoạn nữa. Vì thế bạn chỉ nên làm điều này khi chủ định thay đổi cấu trúc lưu trữ vật lý của bảng.
Bảng dưới đây tóm tắt các loại index và các tình huống tương ứng:
Bảng dưới đây tóm tắt các loại index và các tình huống tương ứng:
| Loại Index | Không chỉ định filegroup | Chỉ định filegroup |
| Non-Clustered non-unique | ||
| - Không chứa partition key | aligned, partition key được thêm vào index | not partitioned, non-aligned |
| - Chứa partition key | partitioned, aligned | not partitioned, non-aligned |
| Non-clustered unique | ||
| - Không chứa partition key | Lỗi, cần thêm partition key vào index | not partitioned, non-aligned |
| - Chứa partition key | partitioned, aligned | not partitioned, non-aligned |
| Clustered non-unique | ||
| - Không chứa partition key | partitioned, aligned, partition key được thêm vào index | |
| - Chứa partition key | partitioned, aligned | |
| Clustered unique | ||
| - Không chứa partition key | Lỗi, cần thêm partition key vào index | |
| - Chứa partition key | partitioned, aligned |
Như vậy bạn thấy index luôn được phân đoạn cùng với bảng chỉ trừ trường hợp với non-clustered index và bạn chỉ định filegroup cho nó. Vì sao SQL Server luôn ưu tiên phân đoạn index? Lý do là vì những lợi ích sau:
- Bạn có thể backup/restore từng đoạn của bảng và index cũng được backup/restore theo.
- Bạn có thể rebuild hoặc reorganize lại index trên từng đoạn mà bạn thấy cần phải rebuild hoặc reorganize
- Bạn có thể thực hiện các thao tác trên từng đoạn dễ dàng như switch/switch out hoặc split/merge
- Bộ optimizer khi sử dụng aligned index có thể áp dụng loại bỏ các đoạn không chứa dữ liệu cần cho câu lệnh
Hãy liên hệ bảng với chiếc bánh tét như đã nêu ở đầu bài, tất cả các thao tác kể trên có thể thực hiện dễ dàng nếu như từng lát bánh có một sợi lạt riêng. Còn nếu vẫn còn một sợi lạt buộc vòng tất cả các lát bánh với nhau (non-aligned index) thì các thao tác này sẽ bị sợi lạt đó làm vướng và không thực hiện được.
- Bạn có thể backup/restore từng đoạn của bảng và index cũng được backup/restore theo.
- Bạn có thể rebuild hoặc reorganize lại index trên từng đoạn mà bạn thấy cần phải rebuild hoặc reorganize
- Bạn có thể thực hiện các thao tác trên từng đoạn dễ dàng như switch/switch out hoặc split/merge
- Bộ optimizer khi sử dụng aligned index có thể áp dụng loại bỏ các đoạn không chứa dữ liệu cần cho câu lệnh
Hãy liên hệ bảng với chiếc bánh tét như đã nêu ở đầu bài, tất cả các thao tác kể trên có thể thực hiện dễ dàng nếu như từng lát bánh có một sợi lạt riêng. Còn nếu vẫn còn một sợi lạt buộc vòng tất cả các lát bánh với nhau (non-aligned index) thì các thao tác này sẽ bị sợi lạt đó làm vướng và không thực hiện được.
Vậy trong trường hợp nào vẫn cần non-aligned index? Khi bạn cần tạo unique index (non-clustered) trên một cột để đảm bảo tính duy nhất cho nó, bạn không có cách nào khác là tạo index trên một filegroup khác và để nó non-aligned với bảng.
Wednesday, October 03, 2018
Partition Bảng Đã Có Sẵn
Bài viết tham khảo tại:http://www.sqlviet.com/blog/partition-bang-da-co-san
Partition Bảng Đã Có Sẵn
Vũ Huy Tâm
Trong loạt bài về phân đoạn bảng và index, tôi đã giới thiệu về kiến trúc phân đoạn của SQL Server. Các ví dụ nêu trong các bài đó đều sử dụng bảng được tạo lần đầu và áp dụng phân đoạn lên nó. Một câu hỏi thường gặp là, làm thế nào để phân đoạn một bảng đã có sẵn dữ liệu? Bảng này có thể chưa được phân đoạn, hoặc đã được phân đoạn theo một cách khác, nay bạn muốn phân đoạn lại theo cách hợp lý hơn.
Có một cách làm thực ra rất đơn giản mà nếu bạn để ý một tẹo là thấy ngay, đó là trong SQL Server clustered index chính là bảng, nói cách khác là bảng được lưu trữ thành một cây index và cây này chính là clustered index. Vì vậy để phân đoạn bảng bạn chỉ việc phân đoạn clustered index của nó, bằng cách tạo lại clustered index trên partition scheme.
Trong trường hợp phổ biến, clustered index thường đi liền với primary key. Ở ví dụ dưới, giả sử bạn đã có bảng BanHang với primary key và clustered index trên trường ID, bảng này không được phân đoạn.
Có một cách làm thực ra rất đơn giản mà nếu bạn để ý một tẹo là thấy ngay, đó là trong SQL Server clustered index chính là bảng, nói cách khác là bảng được lưu trữ thành một cây index và cây này chính là clustered index. Vì vậy để phân đoạn bảng bạn chỉ việc phân đoạn clustered index của nó, bằng cách tạo lại clustered index trên partition scheme.
Trong trường hợp phổ biến, clustered index thường đi liền với primary key. Ở ví dụ dưới, giả sử bạn đã có bảng BanHang với primary key và clustered index trên trường ID, bảng này không được phân đoạn.
CREATE TABLE dbo.BanHang(ID INT IDENTITY NOT NULL, NgayGD DATETIME NOT NULL, MaSP INT NOT NULL, SoLuong INT NOT NULL, Gia INT NOT NULL, CONSTRAINT PK_BanHang PRIMARY KEY CLUSTERED (ID) )
Nay bạn muốn phân đoạn bảng, có hai khả năng xảy ra:
(1) bạn phân đoạn bảng trên trường ID, nghĩa là vẫn giữ nguyên định nghĩa của primary key và clustered index (giữ nguyên trường cũ) chỉ muốn phân đoạn nó ra, dựa vào partition scheme PScheme_ID. Bạn chỉ việc tạo lại index như sau:
(1) bạn phân đoạn bảng trên trường ID, nghĩa là vẫn giữ nguyên định nghĩa của primary key và clustered index (giữ nguyên trường cũ) chỉ muốn phân đoạn nó ra, dựa vào partition scheme PScheme_ID. Bạn chỉ việc tạo lại index như sau:
CREATE UNIQUE CLUSTERED INDEX PK_BanHang ON dbo.BanHang(ID) WITH (DROP_EXISTING = ON) ON PScheme_ID(ID)
Điểm chính của đoạn lệnh trên là mệnh đề “WITH (DROP_EXISTING = ON)”, nó sẽ tự động xây dựng lại index thay vì bạn phải xóa đi rồi tạo lại.
(2) Bạn muốn phân đoạn bảng trên một trường khác, ví dụ NgayGD. Vì trường partition key cần phải có mặt trong clustered index, bạn cần tạo lại clustered index (và primary key) trong đó phải chứa thêm trường NgayGD. Vì định nghĩa của primary key và clustered index khác so với trước, bạn không thể dùng WITH (DROP_EXISTING = ON) được nữa. Nếu cố tình chạy đoạn lệnh này:
(2) Bạn muốn phân đoạn bảng trên một trường khác, ví dụ NgayGD. Vì trường partition key cần phải có mặt trong clustered index, bạn cần tạo lại clustered index (và primary key) trong đó phải chứa thêm trường NgayGD. Vì định nghĩa của primary key và clustered index khác so với trước, bạn không thể dùng WITH (DROP_EXISTING = ON) được nữa. Nếu cố tình chạy đoạn lệnh này:
CREATE UNIQUE CLUSTERED INDEX PK_BanHang ON dbo.BanHang(ID,NgayGD) WITH (DROP_EXISTING = ON) ON PScheme_NGD(NgayGD)
Bạn sẽ gặp lỗi:
Cannot recreate index ‘PK_BanHang’. The new index definition does not match the constraint being enforced by the existing index.
Thay vào đó, bạn cần xóa đi và tạo lại:
Cannot recreate index ‘PK_BanHang’. The new index definition does not match the constraint being enforced by the existing index.
Thay vào đó, bạn cần xóa đi và tạo lại:
ALTER TABLE BanHang DROP CONSTRAINT PK_BanHang GO CREATE UNIQUE CLUSTERED INDEX PK_BanHang ON dbo.BanHang(ID,NgayGD) ON PScheme_NGD(NgayGD) --hoặc ALTER TABLE dbo.BanHang ADD CONSTRAINT PK_BanHang PRIMARY KEY CLUSTERED (ID,NgayGD) ON PScheme_NGD(NgayGD)
Lý do tại sao cần 2 trường ID và NgayGD trong index là vì ID cần thiết để duy trì tính duy nhất, và NgayGD cần có mặt để làm partition key.
Trong trường hợp ít phổ biến hơn, clustered index không đi kèm với primary key, hay nói tổng quát là nó đứng một mình không gắn với một constraint nào. Khi đó bạn có thể tạo lại clustered index với bổ sung thêm trường mới mà vẫn dùng được mệnh đề “WITH (DROP_EXISTING = ON)”:
Trong trường hợp ít phổ biến hơn, clustered index không đi kèm với primary key, hay nói tổng quát là nó đứng một mình không gắn với một constraint nào. Khi đó bạn có thể tạo lại clustered index với bổ sung thêm trường mới mà vẫn dùng được mệnh đề “WITH (DROP_EXISTING = ON)”:
CREATE TABLE dbo.BanHang(ID INT IDENTITY NOT NULL, NgayGD DATETIME NOT NULL, MaSP INT NOT NULL, SoLuong INT NOT NULL, Gia INT NOT NULL ) CREATE UNIQUE CLUSTERED INDEX CI_BanHang ON dbo.BanHang(ID) GO CREATE UNIQUE CLUSTERED INDEX CI_BanHang ON dbo.BanHang(ID,NgayGD) WITH (DROP_EXISTING = ON) ON PScheme_NGD(NgayGD)
Subscribe to:
Posts (Atom)