First I Used AdvantureWork2019 DB you can use it to see the same result that I have, if you want to test it and I'll add a script to Create The Discount Tables at the end and to create the view I used to create the ##tempTables, My question is:
I have 2 Temp tables the first one is This:
DiscID Desc DiscLimitID Limit DiscPercID Perc Qty DiscQtyID Allowed Allocated
1 NA 1 0.0000000 1 0.0200000 1.0000000 1 0.0000000 0.0000000
2 Cheap 2 50.0000000 2 0.0100000 4.0000000 2 1000.0000000 0.0000000
3 Moderate 3 200.0000000 3 0.0250000 3.0000000 3 5000.0000000 0.0000000
4 Expensive 4 1000.0000000 4 0.0500000 2.0000000 4 20000.0000000 5000.0000000
The second one :
SalesOrderID SalesOrderDetailID LineTotal OrderQty ODiscID
43659 1 2024.994000 1 0
43659 2 6074.982000 3 4
43659 3 2024.994000 1 0
43659 4 2039.994000 1 0
43659 5 2039.994000 1 0
43659 6 4079.988000 2 4
43659 7 2039.994000 1 0
43659 8 86.521200 3 0
43659 9 28.840400 1 1
43659 10 34.200000 6 1
I made a query to give me this result (sample) :
SOdID LineTotal DiscID Discount FinalTotal FinalAllocated
9 28.840400 1 0.576808 28.263592 0.576808
10 34.200000 1 0.684000 33.516000 1.260808
11 10.373000 1 0.207460 10.165540 1.468268
18 20.746000 1 0.414920 20.331080 1.883188
12 80.746000 2 0.807460 79.938540 0.807460
19 115.361600 2 1.153616 114.207984 1.961076
29 100.932500 2 1.009325 99.923175 2.970401
85 173.042400 2 1.730424 171.311976 4.700825
105 115.361600 2 1.153616 114.207984 5.854441
139 80.746000 2 0.807460 79.938540 6.661901
34 551.814600 3 13.795365 538.019235 13.795365
44 535.742400 3 13.393560 522.348840 27.188925
95 551.814600 3 13.795365 538.019235 40.984290
104 535.742400 3 13.393560 522.348840 54.377850
2 6074.982000 4 303.749100 5771.232900 303.749100
6 4079.988000 4 203.999400 3875.988600 507.748500
17 1429.408600 4 71.470430 1357.938170 579.218930
20 1445.189800 4 72.259490 1372.930310 651.478420
21 6074.982000 4 303.749100 5771.232900 955.227520
22 4049.988000 4 202.499400 3847.488600 1157.726920
24 1637.400000 4 81.870000 1555.530000 1239.596920
what I want to do now is to make a Cursor or While loop to stop the query from making Discount and adding to the FinalAllocated and replace it to 0
for Example if DiscID = 3 reached 5000(FinalAllocated) then stop the query from continue for DiscID 3 and Add 0 Discount to the rest of the Rows which contain DiscID = 3 then go to the next DiscID which is 4 and start again
Ex:
while (FinalAllocated < Allowed for that ID)
Contnue
Else 0
Here you'll find everything I used to test it if you like
From AdventureWork2019 DB:
Create View vSalesOrderAll As
select SOH.SalesOrderID,SOD.SalesOrderDetailID,SOD.LineTotal,SOH.OrderDate,SOD.OrderQty from sales.SalesOrderDetail as SOD
Inner Join [Sales].[SalesOrderHeader] AS SOH ON
SOD.SalesOrderID = SOH.SalesOrderID
Script for Discount Tables :
USE [AdventureWorks2019]
GO
/****** Object: Table [dbo].[DiscountClass] Script Date: 6/29/2020 12:29:43 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[DiscountClass](
[DiscountClassID] [int] IDENTITY(1,1) NOT NULL,
[Description] [nvarchar](50) NULL,
CONSTRAINT [PK_DiscountClass] PRIMARY KEY CLUSTERED
(
[DiscountClassID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[DiscountLimit] Script Date: 6/29/2020 12:29:43 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[DiscountLimit](
[DiscountLimitID] [int] IDENTITY(1,1) NOT NULL,
[DiscountClassID] [int] NULL,
[Limit] [numeric](24, 7) NULL,
CONSTRAINT [PK_DiscountLimit] PRIMARY KEY CLUSTERED
(
[DiscountLimitID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[DiscountPercentage] Script Date: 6/29/2020 12:29:43 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[DiscountPercentage](
[DiscountPercentageID] [int] IDENTITY(1,1) NOT NULL,
[DiscountLimitID] [int] NULL,
[Quantity] [numeric](24, 7) NULL,
[Percentage] [numeric](24, 7) NULL,
CONSTRAINT [PK_DiscountPercentage] PRIMARY KEY CLUSTERED
(
[DiscountPercentageID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[DiscountQuota] Script Date: 6/29/2020 12:29:43 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[DiscountQuota](
[DiscountQuotaId] [int] IDENTITY(1,1) NOT NULL,
[DiscountClassId] [int] NULL,
[Allowed] [numeric](24, 7) NULL,
[Allocated] [numeric](24, 7) NULL,
CONSTRAINT [PK_DiscountQuota] PRIMARY KEY CLUSTERED
(
[DiscountQuotaId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[DiscountClass] ON
INSERT [dbo].[DiscountClass] ([DiscountClassID], [Description]) VALUES (1, N'NA')
INSERT [dbo].[DiscountClass] ([DiscountClassID], [Description]) VALUES (2, N'Cheap')
INSERT [dbo].[DiscountClass] ([DiscountClassID], [Description]) VALUES (3, N'Moderate')
INSERT [dbo].[DiscountClass] ([DiscountClassID], [Description]) VALUES (4, N'Expensive')
SET IDENTITY_INSERT [dbo].[DiscountClass] OFF
GO
SET IDENTITY_INSERT [dbo].[DiscountLimit] ON
INSERT [dbo].[DiscountLimit] ([DiscountLimitID], [DiscountClassID], [Limit]) VALUES (1, 1, CAST(0.0000000 AS Numeric(24, 7)))
INSERT [dbo].[DiscountLimit] ([DiscountLimitID], [DiscountClassID], [Limit]) VALUES (2, 2, CAST(50.0000000 AS Numeric(24, 7)))
INSERT [dbo].[DiscountLimit] ([DiscountLimitID], [DiscountClassID], [Limit]) VALUES (3, 3, CAST(200.0000000 AS Numeric(24, 7)))
INSERT [dbo].[DiscountLimit] ([DiscountLimitID], [DiscountClassID], [Limit]) VALUES (4, 4, CAST(1000.0000000 AS Numeric(24, 7)))
SET IDENTITY_INSERT [dbo].[DiscountLimit] OFF
GO
SET IDENTITY_INSERT [dbo].[DiscountPercentage] ON
INSERT [dbo].[DiscountPercentage] ([DiscountPercentageID], [DiscountLimitID], [Quantity], [Percentage]) VALUES (1, 1, CAST(1.0000000 AS Numeric(24, 7)), CAST(0.0200000 AS Numeric(24, 7)))
INSERT [dbo].[DiscountPercentage] ([DiscountPercentageID], [DiscountLimitID], [Quantity], [Percentage]) VALUES (2, 2, CAST(4.0000000 AS Numeric(24, 7)), CAST(0.0100000 AS Numeric(24, 7)))
INSERT [dbo].[DiscountPercentage] ([DiscountPercentageID], [DiscountLimitID], [Quantity], [Percentage]) VALUES (3, 3, CAST(3.0000000 AS Numeric(24, 7)), CAST(0.0250000 AS Numeric(24, 7)))
INSERT [dbo].[DiscountPercentage] ([DiscountPercentageID], [DiscountLimitID], [Quantity], [Percentage]) VALUES (4, 4, CAST(2.0000000 AS Numeric(24, 7)), CAST(0.0500000 AS Numeric(24, 7)))
SET IDENTITY_INSERT [dbo].[DiscountPercentage] OFF
GO
SET IDENTITY_INSERT [dbo].[DiscountQuota] ON
INSERT [dbo].[DiscountQuota] ([DiscountQuotaId], [DiscountClassId], [Allowed], [Allocated]) VALUES (1, 1, CAST(0.0000000 AS Numeric(24, 7)), CAST(0.0000000 AS Numeric(24, 7)))
INSERT [dbo].[DiscountQuota] ([DiscountQuotaId], [DiscountClassId], [Allowed], [Allocated]) VALUES (2, 2, CAST(10000.0000000 AS Numeric(24, 7)), CAST(0.0000000 AS Numeric(24, 7)))
INSERT [dbo].[DiscountQuota] ([DiscountQuotaId], [DiscountClassId], [Allowed], [Allocated]) VALUES (3, 3, CAST(50000.0000000 AS Numeric(24, 7)), CAST(0.0000000 AS Numeric(24, 7)))
INSERT [dbo].[DiscountQuota] ([DiscountQuotaId], [DiscountClassId], [Allowed], [Allocated]) VALUES (4, 4, CAST(200000.0000000 AS Numeric(24, 7)), CAST(5000.0000000 AS Numeric(24, 7)))
SET IDENTITY_INSERT [dbo].[DiscountQuota] OFF
GO
Second View :
Create View vAllDiscounts As
select DS.DiscountClassID,DS.Description
,DL.DiscountLimitID,DL.Limit
,DP.DiscountPercentageID,DP.Percentage,DP.Quantity
,DQ.DiscountQuotaId,DQ.Allowed,DQ.Allocated
from DiscountClass DS Inner join
DiscountLimit DL ON DS.DiscountClassID = DL.DiscountClassID
INNER JOIN DiscountPercentage DP ON DL.DiscountLimitID = DP.DiscountLimitID
INNER JOIN DiscountQuota DQ ON DQ.DiscountClassId = DL.DiscountClassID
Temp Tables :
select *
INTO ##TempDiscount
from vAllDiscounts
select *
INTO ##TempOrederData
from vSalesOrderAll
The ODiscId
update TOD
SET TOD.ODiscID = (CASE WHEN TOD.LineTotal <= 49 AND TOD.OrderQty >= 1 THEN 1
WHEN TOD.LineTotal >= 50 AND TOD.LineTotal <= 199 AND TOD.OrderQty >= 4 THEN 2
WHEN TOD.LineTotal >= 200 AND TOD.LineTotal <= 999 AND TOD.OrderQty >= 3 THEN 3
WHEN TOD.LineTotal >= 1000 AND TOD.OrderQty >= 2 THEN 4
ELSE 0 END)
from ##TempOrederData AS TOD
This is the query that I've used to get my output:
WITH TotalDisc AS
(
SELECT TOD.SalesOrderID,TOD.SalesOrderDetailID AS SOdID
,TOD.LineTotal,TOD.OrderQty,TOD.OrderDate,AD.DiscountClassID AS DiscID
,Percentage ,(LineTotal*Percentage) AS Discount, AD.Allocated,AD.Allowed AS Allo
FROM ##TempOrederData TOD
INNER JOIN ##TempDiscount AD ON TOD.ODiscID = AD.DiscountClassID
), TotalAndDiscount AS
(
SELECT SalesOrderID,SOdID,LineTotal,OrderQty,OrderDate,DiscID,Discount,Allocated,LineTotal-Discount AS FinalTotal
FROM TotalDisc
)
SELECT SOdID,LineTotal,DiscID,Discount,FinalTotal,
sum(Discount) over ( Partition by DiscID order by SOdID) AS FinalAllocated
FROM TotalAndDiscount
EDIT :
I Added the Case Statement to stop it when it reach a limit
WITH TotalDisc AS
(
SELECT TOD.SalesOrderID,TOD.SalesOrderDetailID AS SOdID
,TOD.LineTotal,TOD.OrderQty,TOD.OrderDate,AD.DiscountClassID AS DiscID
,Percentage ,(LineTotal*Percentage) AS Discount, AD.Allocated AS Allc,AD.Allowed AS Allo
FROM ##TempOrederData TOD
INNER JOIN ##TempDiscount AD ON TOD.ODiscID = AD.DiscountClassID
), TotalAndDiscount AS
(
SELECT SalesOrderID,SOdID,LineTotal,OrderQty,OrderDate,DiscID,Discount,Allc,LineTotal-Discount AS FinalTotal
FROM TotalDisc
), FinalCalc AS
(
SELECT SOdID,LineTotal,DiscID,Discount,FinalTotal,
sum(Discount) over ( Partition by DiscID order by SOdID) AS FinalAllocated
FROM TotalAndDiscount
),TestAllo AS
(
Select *
,Allocated = SUM(CASE WHEN DiscID = 1 AND FinalAllocated < 5000 THEN FinalAllocated
WHEN DiscID = 2 AND FinalAllocated < 10000 THEN FinalAllocated
WHEN DiscID = 3 AND FinalAllocated < 20000 THEN FinalAllocated
WHEN DiscID = 4 AND FinalAllocated < 50000 THEN FinalAllocated
Else '0' END )
from FinalCalc
group by FinalCalc.SodID,FinalCalc.LineTotal,FinalCalc.DiscID,FinalCalc.Discount,FinalCalc.FinalTotal,FinalCalc.FinalAllocated
)
select * from TestAllo
where Allocated != 0
order by DiscID
Edit 2 :
Desired Output would be something like that
SOdID LineTotal DiscID Discount FinalTotal FinalAllocated Allocated
915 2144.112900 4 107.205645 2036.907255 48226.798765 48226.798765
916 27055.760424 4 1352.788021 25702.972403 49579.586786 49579.586786
918 8159.976000 4 407.998800 7751.977200 49987.585586 49987.585586
924 1749.588000 4 0 1749.588000 49987.585586 0.000000
928 1749.588000 4 0 1749.588000 49987.585586 0.000000
932 1749.588000 4 0 1749.588000 49987.585586 0.000000
934 2097.294500 4 0 2097.294500 49987.585586 0.000000
942 1258.376700 4 0 1258.376700 49987.585586 0.000000
is when Allocated reach the limit I want it to stop the discount and replace the next Discounts with 0 and to take The allowed from the discount table which would be something like that :
CASE WHEN DiscID = 1 AND FinalAllocated < (Select allowed from DiscountQuota where DiscId=1) THEN FinalAllocated
but I keep getting this error
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
Again what i'm trying to achieve is to make a Cursor or While statement to stop the sum when the (FinalAllocated) > Allowed for that ID from table ##TempDiscount
Thank you in Advance
Create the output fields in the temp tables to help with the calculations.
First calculate the FinalAllocated without any restrictions:
WITH FinalAllocatedCTE AS (
SELECT
SalesOrderID
, FinalAllocated
, SUM(Discount) OVER (PARTITION BY DiscID ORDER BY LineTotal ASC) AS Calc_FinalAllocated
FROM
##TempOrederData
)
UPDATE
FinalAllocatedCTE
SET
FinalAllocated = Calc_FinalAllocated
Then reset the records which are over the limits:
UPDATE
DST
SET
FinalAllocated= 0
FROM
##TempOrederData AS DST
INNER JOIN ##TempDiscount AS DISC
ON DST.DiscID = DISC.DiscID
AND DST.FinalAllocated > DISC.Limit
;
Now you can do whatever you want with the records having 0 in FinalAllocated:
DECLARE #MaxAllocated TABLE (DiscID INT, MaxAllocated DECIMAL...);
INSERT INTO MaxAllocated (DiscID, MaxAllocated)
SELECT DiscID, MAX(FinalAllocated) AS MaxAllocated FROM ##TempOrederData
;
UPDATE
DST
SET
DST.FinalAllocated= MA.MaxAllocated
, DST.Discount = 0
, DST.Allocated = 0
FROM
##TempOrederData AS DST
INNER JOIN #MaxAllocated AS MA
ON DST.DiscID = MA.DiscID
WHERE
TotalAllocated = 0
;
I have table Range with columns
Start (date), RangeTypeId (integer), ChannelId (integer), IsActive (bit)
I have this index:
CREATE UNIQUE NONCLUSTERED INDEX [IX_Range_Unique]
ON [dbo].[Range] ([Start] ASC, [RangeTypeId] ASC, [ChannelId] ASC, [IsActive] ASC)
I want my index to prevent inserting or updating only in case of 2 rows with IsActive = 1. So I want index or some sort of trigger that will allow to have multiple Ranges with IsActive = 0 and same start date, channel id and type, but only one with IsActive = 1 and same start date, channel id and type.
Example of valid db table state:
Start | RangeTypeId | ChannelId | IsActive
------------------------------------------
23:00 5 1 0
23:00 5 1 0
23:00 5 1 0
23:00 5 1 1
invalid:
Start | RangeTypeId | ChannelId | IsActive
------------------------------------------
23:00 5 1 0
23:00 5 1 0
23:00 5 1 1
23:00 5 1 1
Is it possible?
You can create a unique filtered index like so:
CREATE UNIQUE NONCLUSTERED INDEX [uIXf_Range_Unique] ON [dbo].[Range]
(
[Start] ASC,
[RangeTypeId] ASC,
[ChannelId] ASC,
[IsActive] ASC
)
where IsActive = 1
rextester demo: http://rextester.com/LBI81243
create table range ([Start] varchar(5), [RangeTypeId] int, [ChannelId] int, [IsActive] int) ;
insert into range ([Start], [RangeTypeId], [ChannelId], [IsActive]) values
('23:00', 5, 1, 0),
('23:00', 5, 1, 0),
('23:00', 5, 1, 0),
('23:00', 5, 1, 1)
;
CREATE UNIQUE NONCLUSTERED INDEX [uIXf_Range_Unique] ON [dbo].[Range]
(
[Start] ASC,
[RangeTypeId] ASC,
[ChannelId] ASC,
[IsActive] ASC
)
where IsActive = 1
go
/* throws an error error due to duplicate key */
insert into range ([Start], [RangeTypeId], [ChannelId], [IsActive]) values
('23:00', 5, 1, 1)
IMHO,There is one big disadvantage of UNIQUE FILTERED INDEX .
Main purpose of index is to speed up select query.
So it is possible that above index are not utilize in most of the select query,in that case we often change index .
So main purpose of index is defeated.In that case we drop the index and create index on some other column/columns.
Idea of Filtered index is also different than use here.Purpose of filtered index is that if among huge data,we query on certain value very frequently then we create filtered index on that column using that value like above.Its purpose is not to provide uniqueness.
Suppose DBA is not aware of this plan and DBA decide to drop this index then you may start getting duplicate records.
So best way to check duplicate in this case will be to check via code .
if not exists (select id from mytable where [Start]=#Start and [RangeTypeId]=#RangeTypeId
and [ChannelId]=#ChannelId and [IsActive]=1)
BEGIN
print 'insert'
END
If insert/update can happen from several places then it is wise to use trigger.
I would like to migrate DDL from Oracle to SQLServer.
It was able to migrate to a certain extent.
However, some items can not be migrated.
Oracle DDL:
CREATE TABLE ExampleTbl
(
code CHAR(3) NOT NULL,
code2 CHAR(3) NOT NULL,
username VARCHAR2(255) NOT NULL,
d DATETIME
CONSTRAINT PK_Example PRIMARY KEY (code, code2) USING INDEX
PCTFREE 10
INITRANS 2 -- <-?
MAXTRANS 255 -- <-?
TABLESPACE TBSP01
STORAGE(INITIAL 64K NEXT 1M MINEXTENTS 1 MAXEXTENTS 2147483645 BUFFER_POOL DEFAULT) -- <-?
LOGGING -- <-?
ENABLE -- <-?
)
PCTFREE 10
MAXTRANS 255
TABLESPACE TBSP01
STORAGE(INITIAL 64K NEXT 1M MINEXTENTS 1 MAXEXTENTS 2147483645 BUFFER_POOL DEFAULT) -- <-?
NOCACHE -- <-?
LOGGING
/
COMMENT ON TABLE ExampleTbl IS 'Table comment!'
/
SQLServer DDL:
CREATE TABLE [dbo].[ExampleTbl](
[code] [char](10) NOT NULL,
[code2] [char](10) NOT NULL,
[username] [varchar](255) NOT NULL,
[d] [datetime] NULL,
CONSTRAINT [PK_ExampleTbl] PRIMARY KEY CLUSTERED
(
[code] ASC,
[code2] ASC
)
WITH
(
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON,
FILLFACTOR = 90 -- FillFactor = 100 - Oracle.PCTFREE(10)
) ON [TBSP01] -- Oracle.TableSpace
) ON [TBSP01] -- Oracle.TableSpace
GO
EXEC sys.sp_addextendedproperty
#name=N'MS_Description',
#value=N'Table comment!' , -- Oracle.Comment
#level0type=N'SCHEMA',
#level0name=N'dbo',
#level1type=N'TABLE',
#level1name=N'ExampleTbl'
GO
Don't worry about column names.
How do I migrate these?
INITRANS, MAXTRANS, STORAGE, LOGGING, ENABLE, NOCACHE.
And, are there any other problems?
CREATE TABLE Statement
Converting CREATE TABLE statement keywords and clauses:
Oracle SQL Server
1 ENABLE constraint attribute Removed
Storage and physical attributes:
Oracle SQL Server
1 PCTFREE num Removed
2 PCTUSED num Removed
3 INITRANS num Removed
4 MAXTRANS num Removed
5 COMPRESS [BASIC] | COMPRESS num | NOCOMPRESS Removed
6 LOGGING | NOLOGGING Removed
7 SEGMENT CREATION IMMEDIATE | DEFERRED Removed
8 TABLESPACE name ON name
9 LOB (column) STORE AS BASIC FILE (params) Removed
10 PARALLEL num | NOPARALLEL Removed
11 NOCACHE Removed
12 NOMONITORING Removed
STORAGE clause:
Oracle SQL Server
1 INITIAL num Removed
2 NEXT num Removed
3 MINEXTENTS num Removed
4 MAXEXTENTS num | UNLIMITED Removed
5 PCTINCREASE num Removed
6 FREELISTS num Removed
7 FREELIST GROUPS num Removed
8 BUFFER_POOL DEFAULT | KEEP | RECYCLE Removed
9 FLASH_CACHE DEFAULT | KEEP | NONE Removed
10 CELL_FLASH_CACHE DEFAULT | KEEP | NONE Removed
LOB storage clause:
Oracle SQL Server
1 TABLESPACE name Removed
2 DISABLE | ENABLE STORAGE IN ROW Removed
3 CHUNK num Removed
4 NOCACHE Removed
5 LOGGING Removed
More Details http://www.sqlines.com/oracle-to-sql-server
I have a client table that has a calculated column for Age.
CREATE TABLE [dbo].[Client]
(
[ClientID] [int] IDENTITY(1,1) NOT NULL,
[ClientName] varchar(50) NOT NULL,
[DOB] [date] NOT NULL,
[Age] AS (datediff(year,[DOB],getdate())-case when dateadd(year,datediff(year,[DOB],getdate()),[DOB])>getdate() then (1) else (0) end),
CONSTRAINT [PK_Client] PRIMARY KEY CLUSTERED
(
[ClientID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
I then having an AgeGroup table that has the following rows.
AgeGroupID AgeGroup StartRange EndRange
1 65 & over 65 255
2 55-64 55 64
3 45-54 45 54
4 35-44 35 44
5 25-34 25 34
6 19-24 19 24
7 13-18 13 18
8 Below 12 0 12
How could I link Client to AgeGroup to show a clients AgeGroup in a select query ?
With the output being returned such as the following
ClientID ClientName Age AgeGroup
1 Peter Smith 25 25-34
Maybe, something like this:
SELECT c.ClientID
,c.ClientName
,c.Age
,ag.AgeGroup
FROM client c
LEFT JOIN AgeGroup ag
ON c.age BETWEEN ag.StartRange AND ag.EndRange
I'm in a big trouble with one database instance of a MS SQL Server 2008 R2! I have the structure bellow that I've created it to simulate my real problem.
USE [sor]
GO
ALTER DATABASE [sor] SET ALLOW_SNAPSHOT_ISOLATION on;
ALTER DATABASE [sor] SET READ_COMMITTED_SNAPSHOT on;
CREATE TABLE [dbo].[test] (
[name] [nvarchar](50) NOT NULL,
[id] [int] NOT NULL,
CONSTRAINT [PK_test] PRIMARY KEY CLUSTERED ([id] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_test] ON [dbo].[test] (
[name] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ALTER TABLE [dbo].[test] ADD CONSTRAINT [PK_test] PRIMARY KEY CLUSTERED (
[id] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE UNIQUE NONCLUSTERED INDEX [pk_key] ON [dbo].[test] (
[id] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
My problem is that when I execute two or more sessions (transactions) concurrently I'm being locked by some database indexes. To simulate it I start one sqlcmd:
-- sqlcmd 1
begin tran s1
insert into dbo.test (id, name) values(1, 'bob');
go
and start another sqlcmd to be the next concurrent transaction:
-- sqlcmd 2
begin tran s2
insert into dbo.test (id, name) values(2, 'john');
go -- locking here!!!!
when I run the go inside the transaction named s2 I'm being locked.
In order to discover what is going on I run
SELECT * FROM sys.dm_exec_requests where DB_NAME(database_id)='sor' and blocking_session_id <>0
session_id request_id start_time status command sql_handle statement_start_offset statement_end_offset plan_handle database_id user_id connection_id blocking_session_id wait_type wait_time last_wait_type wait_resource open_transaction_count open_resultset_count transaction_id context_info percent_complete estimated_completion_time cpu_time total_elapsed_time scheduler_id task_address reads writes logical_reads text_size date_first quoted_identifier arithabort ansi_null_dflt_on ansi_defaults ansi_warnings ansi_padding ansi_nulls concat_null_yields_null transaction_isolation_level lock_timeout deadlock_priority row_count prev_error nest_level granted_query_memory executing_managed_code group_id query_hash query_plan_hash
---------- ----------- ----------------------- -------------- ---------------- --------------------------------------------------------------- ---------------------- -------------------- --------------------------------------------------- ----------- ----------- ------------------------------------ ------------------- ----------- ----------- ---------------- ------------------------------------------ ---------------------- -------------------- -------------------- -------------- ---------------- ------------------------- ----------- ------------------ ------------ ------------------ -------------------- -------- -------------- ----------- ---------- ----------------- ---------- ----------------- ------------- ------------- ------------ ---------- ----------------------- --------------------------- ------------ ----------------- -------------------- ----------- ----------- -------------------- ---------------------- ----------- ------------------ ------------------
74 0 2014-10-01 10:56:54.143 suspended INSERT 0x0200000098A4BD0B49707895A7295A343EF775E7CF23BCF2 50 -1 0x0600170098A4BD0B4001C69D000000000000000000000000 23 1 E24BB9F3-D12A-44EC-944E-FDA206590705 73 LCK_M_X 1762594 LCK_M_X KEY: 23:72057594038910976 (020068e8b274) 3 1 243137590 0x 0 0 0 1762596 1 0x000000018281E2C8 0 0 4 4096 7 0 0 1 0 1 1 1 1 2 -1 0 0 0 0 0 0 2 0xF22D2FE93F4C59F0 0x354B4D3AA833139F
and running
SELECT t1.resource_type, t1.resource_database_id, t1.resource_associated_entity_id, t1.request_mode, t1.request_session_id, t2.blocking_session_id, o1.name 'object name', o1.type_desc 'object descr', p1.partition_id 'partition id', p1.rows 'partition/page rows', a1.type_desc 'index descr', a1.container_id 'index/page container_id' FROM sys.dm_tran_locks as t1 INNER JOIN sys.dm_os_waiting_tasks as t2 ON t1.lock_owner_address = t2.resource_address LEFT OUTER JOIN sys.objects o1 on o1.object_id = t1.resource_associated_entity_id LEFT OUTER JOIN sys.partitions p1 on p1.hobt_id = t1.resource_associated_entity_id LEFT OUTER JOIN sys.allocation_units a1 on a1.allocation_unit_id = t1.resource_associated_entity_id
I have the following
resource_type resource_database_id resource_associated_entity_id request_mode request_session_id blocking_session_id object name object descr partition id partition/page rows index descr index/page container_id
---------------- -------------------- ----------------------------- -------------- ------------------ ------------------- ------------- ------------- ------------- -------------------- -------------- -----------------------
KEY 23 72057594038910976 X 74 73 NULL NULL NULL NULL IN_ROW_DATA 72057594038255616
Once I use READ COMMITED SNAPSHOT I ask you why the sql server is locking the index? what can I do? I cannot delete the indexes! I need them.
There is quite a lot of information out there, but the answer by Martin Smith to this question might shed some light:
Why insert TSQL statement block when transaction isolation level for another transaction is serializable with non-conflicting filter?
It looks like with locking enabled and with potentially only 0 or 1 record in your table then effectively the whole table is locked.
For the real life problem are you how many entry's are in the table you're inserting into? Also is there additional code that that causes the lock to be held for a lot longer than it could be?