Why does my row size exceed the allowed maximum of 8060 bytes - sql

I have the following table in SQL Server 2012, Web Edition:
CREATE TABLE [dbo].[MyTable]
(
[Id] INT IDENTITY (1, 1) NOT NULL,
[Created] DATETIME DEFAULT (getdate()) NOT NULL,
[RefId] INT NULL,
[Name] NVARCHAR (128) NULL,
[Email] NVARCHAR (128) NULL,
[ImageUrl] NVARCHAR (256) NULL,
[Url] VARCHAR (256) NULL,
[Age] TINYINT NULL,
[Country] VARCHAR (6) NULL,
[Location] NVARCHAR (192) NULL,
[People] INT NULL,
[Categories] NVARCHAR (128) NULL,
[Block] BIT DEFAULT ((0)) NOT NULL,
[GeneratedRevenue] INT NULL,
[IsFemale] BIT DEFAULT ((1)) NULL,
[HasInstalled] BIT NULL,
[Keywords] VARCHAR (128) NULL,
[Brands] NVARCHAR (512) NULL,
[Source] TINYINT NULL,
[Alias] VARCHAR (65) NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
As far as I gather, the total size should be 3175 bytes; but I regularly get the following error, when updating the table:
Cannot create a row of size 8068 which is greater than the allowable maximum row size of 8060.
How does the above result in a row size of 8068?
Edit: I should mention that this table has been altered, uses Change Tracking and has four indexes.
Also, if I copy the contents to a new table with the same definition, no errors occur for a while, but do come back.

You say you use Change Tracking. Are you - by any chance - ignoring the versioning part of Change Tracking, and resetting the entries by doing the following?
ALTER TABLE dbo.MyTable disable change_tracking
ALTER TABLE dbo.MyTable enable change_tracking
If so, you may have a suspect. Change Tracking adds an 8 bit column behind the scenes every time you reenable Change Tracking, which is dropped if it already exists. Since dropping a column is just a meta operation, you may have a large number of dropped 8 bit columns lurking behind the scenes, depending on the frequency with which you reenable Change Tracking.
To check this, look at the system_internals_partition_columns view and see if you have a large number of is_dropped colums. There could be more reasons for having many of those, but this way of using Change Tracking is one of them.
I see Remus Rusanu is linking to a good article in a comment (rusanu.com/2011/10/20/sql-server-table-columns-under-the-hoo‌​d): the queries he lists should be what you need to see if the above is the case.
Edit:
In case you need to delete the dropped columns, you can rebuild the clustered index for the table(s) that have many dropped columns. This means that rebuilding the clustered index for MyTable will relieve you of your symptom.

Related

How to design database for storage of Items in Inventory stores?

This is basically for a Production Management System that I'm currently working on. I have more than one type of Items to store in inventory (Products/Parts/Materials). I want to store them in Inventory stores which are composed of(Store, Section, Rack, Bin). I'm stuck while completing this design and want help from you people to brainstorm some ideas on how I can complete this design.
This is what I have right now.
Products:
[ID] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (50) NOT NULL,
[DefaultPurchasePrice] FLOAT (53) NOT NULL,
[DefaultSalePrice] FLOAT (53) NOT NULL,
[Description] TEXT NULL,
[ItemCode] NVARCHAR (50) NOT NULL,
[MinimumLevel] INT NOT NULL,
[DateAdded] DATE NOT NULL,
[LastModified] DATETIME NOT NULL
Stores:
[ID] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (50) NOT NULL
Storages:
[ID] INT IDENTITY (1, 1) NOT NULL,
[StoreID] INT NOT NULL,
[Section] INT DEFAULT ((0)) NOT NULL,
[Rack] INT DEFAULT ((0)) NOT NULL,
[Shelf] INT DEFAULT ((0)) NOT NULL,
[Bin] INT DEFAULT ((0)) NOT NULL,
PRIMARY KEY CLUSTERED ([ID] ASC),
CONSTRAINT [FK_Storages_Stores] FOREIGN KEY ([StoreID]) REFERENCES [dbo].
[Stores] ([ID])
ProductStock:
[ProductID] INT NOT NULL,
[QuantityOnHand] INT DEFAULT ((0)) NOT NULL,
[QuantityAllocated] INT DEFAULT ((0)) NOT NULL,
[QuantityTarget] INT DEFAULT ((0)) NOT NULL,
[LastModified] DATETIME NOT NULL,
PRIMARY KEY CLUSTERED ([ProductID] ASC),
CONSTRAINT [FK_ProductStock_Product] FOREIGN KEY ([ProductID]) REFERENCES [dbo].[Products] ([ID])
Problem with this design is that I'm not able to track the lotts.
First, this is a really broad question, so don't be too surprised if it gets flagged for that.
With regard to what you have so far, though, I see a couple of minor issues.
Products.Description is TEXT. The TEXT data type has been deprecated for quite some time now, so you should change that to NVARCHAR(MAX).
ProductStock only allows for three states for an item, and I'm not sure what QuantityTarget is supposed to mean. If it's an ideal inventory number, doesn't that belong in the Products table, along with MinimumLevel? I assume that QuantityOnHand means sellable units and QuantityAllocated means items that have been ordered by a customer.
If you're interested in transactional data, though, you'll need to add at least two more fields; QuantityOnOrder (from the vendor) and something like QuantityInactive, where you'll account for things like units that were damaged on receipt and are awaiting return to the vendor. Basically any units you own, but can't sell.
That said, your data model is still missing quite a lot of entities if you want to know where something came from and where it went. On the incoming side, you haven't created Vendors, CommonCarriers, PurchaseOrders, PurchaseOrderDetails, or Receipts. On the outgoing side, you haven't created Customers, Invoices, InvoiceDetails, Returns, or ReturnDetails. And we haven't even touched on payment types, or probably twenty other things I'm not thinking of off the top of my head.
There are tons of resources on line for this kind of data modeling. The ARTS Retail Operational Data Model is just one, but it's very comprehensive and should give you some good pointers for further research.

SQL Server: Using binary_checksum column on a table best practice

Looking at table definitions on an SQL server database have noticed that the (1) binary_checksum column sometimes includes the primary key my_table_id and sometimes does not. What is the best practice?
(2) Also what about the update_by and update_timestamp should they be included or not?
CREATE TABLE [dbo].[my_table] (
[my_table_id] SMALLINT NOT NULL PRIMARY KEY,
[a] SMALLINT NOT NULL,
[b] CHAR(25) NOT NULL,
[update_timestamp] DATETIME NOT NULL DEFAULT getdate(),
[update_by] CHAR(8) NOT NULL,
[my_checksum_col] AS (binary_checksum([a], [g], [update_by], [update_timestamp]))
)
VS
CREATE TABLE [dbo].[my_table] (
[my_table_id] SMALLINT NOT NULL PRIMARY KEY,
[a] SMALLINT NOT NULL,
[b] CHAR(25) NOT NULL,
[update_timestamp] DATETIME NOT NULL DEFAULT getdate(),
[update_by] CHAR(8) NOT NULL,
[my_checksum_col] AS (binary_checksum([my_table_id],[a], [g], [update_by], [update_timestamp]))
)
This may be a matter of opinion, but it depends on how the checksum is going to be used. If the primary key is auto-generated (such as an identity or newid() column), then including it in the checksum is not very interesting. At least, you can't use the checksum to find duplicates.
If the primary key is a data key provided externally, then it is functioning as both data and as a primary key. In that case, including it in the checksum makes more sense.

Why is my query slower when I use the index I made for it?

I have the following table, made with EntityFramework 6.1:
CREATE TABLE [dbo].[MachineryReading] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Location] [sys].[geometry] NULL,
[Latitude] FLOAT (53) NOT NULL,
[Longitude] FLOAT (53) NOT NULL,
[Altitude] FLOAT (53) NULL,
[Odometer] INT NULL,
[Speed] FLOAT (53) NULL,
[BatteryLevel] INT NULL,
[PinFlags] BIGINT NOT NULL,
[DateRecorded] DATETIME NOT NULL,
[DateReceived] DATETIME NOT NULL,
[Satellites] INT NOT NULL,
[HDOP] FLOAT (53) NOT NULL,
[MachineryId] INT NOT NULL,
[TrackerId] INT NOT NULL,
[ReportType] NVARCHAR (1) NULL,
[FixStatus] INT DEFAULT ((0)) NOT NULL,
[AlarmStatus] INT DEFAULT ((0)) NOT NULL,
[OperationalSeconds] INT DEFAULT ((0)) NOT NULL,
CONSTRAINT [PK_dbo.MachineryReading] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.MachineryReading_dbo.Machinery_MachineryId] FOREIGN KEY ([MachineryId]) REFERENCES [dbo].[Machinery] ([Id]) ON DELETE CASCADE,
CONSTRAINT [FK_dbo.MachineryReading_dbo.Tracker_TrackerId] FOREIGN KEY ([TrackerId]) REFERENCES [dbo].[Tracker] ([Id]) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX [IX_MachineryId]
ON [dbo].[MachineryReading]([MachineryId] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_TrackerId]
ON [dbo].[MachineryReading]([TrackerId] ASC);
Thats a lot of information, and our most common (and slowest) query only uses a subset of it:
SELECT TOP 1 OperationalSeconds
FROM MachineryReading
WHERE MachineryId = #id
AND DateRecorded > #start
AND DateRecorded < #end
AND OperationalSeconds <> 0
The table stores a few million rows, recorded from about 2012 onwards, although our code is set to begin some searches from 2000. It was running pretty slowly, so one of the guys I work with partitioned the table based on DateRecorded:
ALTER PARTITION SCHEME PartitionSchemeMonthRange NEXT USED [Primary]
ALTER PARTITION FUNCTION [PartitionFunctionMonthRange]() SPLIT RANGE(N'2016-01-01T00:00:00.000')
ALTER PARTITION SCHEME PartitionSchemeMonthRange NEXT USED [Primary]
ALTER PARTITION FUNCTION [PartitionFunctionMonthRange]() SPLIT RANGE(N'2016-02-01T00:00:00.000')
...
CREATE UNIQUE CLUSTERED INDEX [PK_dbo.MachineryReadingPs] ON MachineryReading(DateRecorded, Id) ON PartitionSchemeMonthRange(DateRecorded)
However, the query above is still running pretty slowly. So on top of that, I made another index:
CREATE NONCLUSTERED INDEX [IX_MachineryId_DateRecorded]
ON [dbo].[MachineryReading]([DateRecorded] ASC, [MachineryId] ASC)
INCLUDE([OperationalSeconds], [FixStatus]);
Executing that query again, the execution plan shows it completely ignoring the index I just made, instead opting for Constant Scan, and Index Seek on IX_MachineryId. This works pretty quickly for a small date range, but is terrible for getting the total operational hours.
Ok, I can deal with that: WITH(INDEX(IX_MachineryId_DateRecorded)).
Nope. It actually runs significantly slower, when using the index I made specifically for that query! What gives? What can I do better?
You have DateRecorded before MachineryId in your indexes. Reverse these for a more efficient index.

Save records in rows or columns in SQL Server

I have to save three document types in a table. number of document types is fixed and will not change. there is more than 1 million records and in the future it can be more than 100 millions. for this purpose performance is so important in my program. I don't know which way can improve the database performance. row-based or column based?
Row-Based:
CREATE TABLE [Person].[Document]
(
[Id] [uniqueidentifier] NOT NULL,
[PersonId] [uniqueidentifier] NOT NULL,
[Document] [varbinary](max) NULL,
[DocType] [int] NOT NULL,
)
Column-based:
CREATE TABLE [Person].[Document]
(
[Id] [uniqueidentifier] NOT NULL,
[PersonId] [uniqueidentifier] NOT NULL,
[Document_Page1] [varbinary](max) NULL,
[Document_Page2] [varbinary](max) NULL,
[Document_Page3] [varbinary](max) NULL,
)
The normalized (or as you called it - row based) solution is more flexible.
It allows you to change the number of documents saved for each person without changing the database structure, and usually is the preferred solution.
A million rows is a small table for SQL server.
I've seen database tables with 50 million rows that performs very well.
It's a question of correct indexing.
I do suggest that if you want better performance use an int identity column for your primary key instead of a uniqueidentifier, since it's very light weight and much easier for the database to index because it's not randomly ordered to begin with.
I would go with the normalized solution.

Unique Constraint in Visual Studio SQL Designer

I was looking at ways to make a newly added column a Unique but not primary column. I had a look at this W3School link. But Instead of following their approach I simply changed my table in the Visual Studio designer as.
CREATE TABLE [dbo].[Userpro] (
[Id] INT NOT NULL IDENTITY,
[Name] NVARCHAR (50) NULL,
[Postcode] NVARCHAR (4) NULL,
[Gender] INT NULL,
[Blog] NVARCHAR (MAX) NULL,
[FeedBack] NVARCHAR (MAX) NULL,
[Username] NVARCHAR (50) NOT NULL Unique,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
Notice that I simply added "Unique" [Username] NVARCHAR (50) NOT NULL Unique. I am unsure if this has the same effect or should I go back and just use the script in the link.
That is perfect.
Adding UNIQUE will have the effect you describe. It is also explained in the link you provide.