Reputation: 507
I am using SQL Server and I have a table like this one
CREATE TABLE dbo.CompanyRolesExpanded (
StaticId uniqueidentifier NOT NULL,
UserId uniqueidentifier NULL,
UserGroupId uniqueidentifier NULL,
CompanyId uniqueidentifier NULL,
CompanyGroupId uniqueidentifier NULL,
CompanyAccessUnitRole uniqueidentifier NULL,
PRIMARY KEY CLUSTERED (StaticId)
)
GO
For now, this table around 3 millions rows. A simple select like this one takes around 30 seconds
SELECT UserId,UserGroupId
,CompanyId,CompanyGroupId
,CompanyAccessUnitRole
FROM CompanyRolesExpanded
Is there a way to improve it?
Upvotes: 2
Views: 1108
Reputation: 46425
I don't think guids are the long pole in the tent from a performance perspective in this case. Running the PowerShell test below of a 3M row select from a remote server, the results showed the int test is about 10% faster on average. Assuming similar results in your environment, that would translate to 27 seconds with int versus 30 with guid. I observed most of the time was due to client CPU processing of the large result set.
This isn't to say there aren't considerations with guids, especially on a single-disk spinning media storage, but I wanted to make it clear that it's the large result set that is the issue rather than the data type.
$connectionString = "Data Source=YourServer;Initial Catalog=tempdb;Integrated Security=SSPI;Application Name=PerformanceTestScript";
$guidSetupScript = @"
CREATE TABLE dbo.Example (
StaticId uniqueidentifier NOT NULL,
UserId uniqueidentifier NULL,
UserGroupId uniqueidentifier NULL,
CompanyId uniqueidentifier NULL,
CompanyGroupId uniqueidentifier NULL,
CompanyAccessUnitRole uniqueidentifier NULL,
PRIMARY KEY CLUSTERED (StaticId)
);
WITH
t10 AS (SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t(n))
,t1k AS (SELECT 0 AS n FROM t10 AS a CROSS JOIN t10 AS b CROSS JOIN t10 AS c)
,t10m AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS num FROM t1k AS a CROSS JOIN t1k AS b CROSS JOIN t1k AS c CROSS JOIN t10 AS d)
INSERT INTO dbo.Example WITH(TABLOCKX) (
StaticId
, UserId
, UserGroupId
, CompanyId
, CompanyGroupId
, CompanyAccessUnitRole
)
SELECT
NEWID()
, NEWID()
, NEWID()
, NEWID()
, NEWID()
, NEWID()
FROM t10m
WHERE num <= 3000000;
"@
$intSetupScript = @"
CREATE TABLE dbo.Example (
StaticId int NOT NULL,
UserId int NULL,
UserGroupId int NULL,
CompanyId int NULL,
CompanyGroupId int NULL,
CompanyAccessUnitRole int NULL,
PRIMARY KEY CLUSTERED (StaticId)
);
WITH
t10 AS (SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t(n))
,t1k AS (SELECT 0 AS n FROM t10 AS a CROSS JOIN t10 AS b CROSS JOIN t10 AS c)
,t10m AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS num FROM t1k AS a CROSS JOIN t1k AS b CROSS JOIN t1k AS c CROSS JOIN t10 AS d)
INSERT INTO dbo.Example WITH(TABLOCKX) (
StaticId
, UserId
, UserGroupId
, CompanyId
, CompanyGroupId
, CompanyAccessUnitRole
)
SELECT
num
, num
, num
, num
, num
, num
FROM t10m
WHERE num <= 3000000;
"@
try
{
$values = [System.Array]::CreateInstance([System.Object], 6)
$connection = New-Object System.Data.SqlClient.SqlConnection($connectionString)
$command = New-Object System.Data.SqlClient.SqlCommand
$command.CommandTimeout = 0
$connection.Open()
$command.Connection = $connection
#Guid setup
$command.CommandText = "IF OBJECT_ID(N'dbo.Example') IS NOT NULL DROP TABLE dbo.Example;"
[void]$command.ExecuteNonQuery()
$command.CommandText = $guidSetupScript
[void]$command.ExecuteNonQuery()
#guid test
$testSw = [System.Diagnostics.StopWatch]::StartNew()
Write-Host "Starting Guid test."
$command.CommandText = "SELECT * FROM dbo.Example;"
$reader = $command.ExecuteReader()
while($reader.Read()) {
$values = $reader.GetValues($values)
}
$reader.Close()
$testSw.Stop()
Write-Host "Guid test duration was $($testSw.Elapsed.ToString())"
#int setup
$command.CommandText = "IF OBJECT_ID(N'dbo.Example') IS NOT NULL DROP TABLE dbo.Example;"
[void]$command.ExecuteNonQuery()
$command.CommandText = $intSetupScript
[void]$command.ExecuteNonQuery()
#int test
$testSw = [System.Diagnostics.StopWatch]::StartNew()
Write-Host "Starting int test."
$command.CommandText = "SELECT * FROM dbo.Example;"
$reader = $command.ExecuteReader()
while($reader.Read()) {
$values = $reader.GetValues($values)
}
$reader.Close()
$testSw.Stop()
Write-Host "Int test duration was $($testSw.Elapsed.ToString())"
$connedtion.Close()
}
catch [Exception]
{
throw
}
Upvotes: 3
Reputation: 17146
While the full context of question ( like execution plan, indexes ) are unknown, I'm tempted to put down a rather large list of downfalls associated with GUIDs as my answer.
All column in your table have a GUID.
StaticId uniqueidentifier NOT NULL,
UserId uniqueidentifier NULL,
UserGroupId uniqueidentifier NULL,
CompanyId uniqueidentifier NULL,
CompanyGroupId uniqueidentifier NULL,
CompanyAccessUnitRole uniqueidentifier NULL
To quote the cons from a source where author favors GUIDs
GUID Cons
- It is a whopping 4 times larger than the traditional 4-byte index value; this can have serious performance and storage implications if you're not careful
- Cumbersome to debug where userid='{BAE7DF4-DDF-3RG-5TY3E3RF456AS10}'
- The generated GUIDs should be partially sequential for best performance (eg, newsequentialid() on SQL 2005) and to enable use of clustered indexes
Your data will be spread across more pages and will have more physical reads compared to using say int for Key.
If you do many number of inserts/updates/deletes, your indexes will be highly fragmented. This is so because the GUIDs are generated at random and it takes a toll at engine to update index for organizing them in a sequential order.
My bet is that your indexes need to be rebuilt. Here's an article which compares GUID with INT column indexes and reflects that GUID is slower than INT but can be improved and brought at par on index rebuilding.
If you think that GUID are the culprit, I'd suggest that you should look at bigint
as an option
Upvotes: 2