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 am using SQL Server 2014.
I am having following table which contains data for stocks minute wise.
CREATE TABLE [dbo].[HISTORICALDATA]
(
[ID] [INT] IDENTITY(1,1) NOT NULL,
[SYMBOL] [NVARCHAR](50) NULL,
[DATETIME] [DATETIME] NULL,
[O] [DECIMAL](18, 2) NULL,
[H] [DECIMAL](18, 2) NULL,
[L] [DECIMAL](18, 2) NULL,
[C] [DECIMAL](18, 2) NULL,
[V] [INT] NULL,
CONSTRAINT [PK_HISTORICALDATA]
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
Tables contains data as following sample shown
SYMBOL DATETIME O H L C V
-----------------------------------------------------------------
ACC 2019-01-01 09:16:00.000 1512.30 1512.30 1507.00 1507.00 7398
ACC 2019-01-01 09:17:00.000 1506.35 1507.80 1503.00 1504.05 8120
ACC 2019-01-01 09:18:00.000 1504.25 1506.00 1504.00 1505.50 3487
ACC 2019-01-01 09:19:00.000 1505.00 1505.00 1503.10 1504.00 3138
ACC 2019-01-01 09:20:00.000 1504.95 1504.95 1501.20 1501.20 4567
ACC 2019-01-01 09:21:00.000 1500.30 1500.95 1498.00 1498.00 8522
ACC 2019-01-01 09:22:00.000 1498.00 1500.90 1498.00 1500.05 4462
ACC 2019-01-01 09:23:00.000 1500.85 1501.40 1500.00 1500.95 2006
ACC 2019-01-01 09:24:00.000 1500.85 1500.95 1500.00 1500.05 1400
ACC 2019-01-01 09:25:00.000 1500.90 1500.95 1499.00 1500.00 3484
ACC 2019-01-01 09:26:00.000 1499.20 1500.00 1499.00 1499.00 1960
ACC 2019-01-01 09:27:00.000 1499.60 1499.95 1498.00 1498.90 1903
ACC 2019-01-01 09:28:00.000 1498.50 1498.50 1496.40 1498.00 3060
ACC 2019-01-01 09:29:00.000 1497.05 1499.10 1496.75 1499.10 3522
ACC 2019-01-01 09:30:00.000 1499.35 1499.50 1498.35 1498.35 741
I tried with following query but it gave me wrong results
SELECT
SYMBOL,
DATEADD(MINUTE, 1, MAX(DATETIME)),
MAX(H),
MIN(L),
SUM(V)
FROM
HISTORICALDATA
WHERE
SYMBOL = 'ACC' AND MONTH(DATETIME) = 1
GROUP BY
SYMBOL,
DATEDIFF(MINUTE, 0 ,DATETIME) / 15
ORDER BY
DATEDIFF(MINUTE, 0, DATETIME) / 15
I need out with each 15 minute interval with
SYMBOL, DATETIME, OPENOF15MIN, HIGHOF15MIN, LOWOF15MIN, CLOSEOF15MIN
If someone can help.
Thanks
Perhaps this can help.
Example dbFiddle
Select [SYMBOL]
,DTR1 = min(case when RNO=1 then [DateTime] end)
,DTR2 = min(case when RNC=1 then [DateTime] end)
,O = min(case when RNO=1 then O end)
,C = min(case when RNC=1 then C end)
,H = max(H)
,L = min(L)
,V = sum(V)
From (
Select *
,Grp = Dense_Rank() over (Partition By Symbol Order By tMin )
,RNO = Row_Number() over (Partition By Symbol,tMin Order By [DateTime])
,RNC = Row_Number() over (Partition By Symbol,tMin Order By [DateTime] Desc)
From YourTable A1
Cross Apply ( values ( (DateDiff(MINUTE,0,[DATETIME]) - 1) / 15 ) ) A2(tMin)
) A
Group By Symbol,Grp
Returns
SYMBOL DTR1 DTR2 O C H L V
ACC 2019-01-01 09:16:00 2019-01-01 09:30:00 1512.30 1498.35 1512.30 1496.40 57770.00
I am novice to SQL and
I have two tables Ticket and TicketAttributes with following Schema
CREATE TABLE [dbo].[Ticket](
[TicketID] [int] IDENTITY(1,1) NOT NULL, --Primary key
[Category] [varchar](256) NOT NULL,
[Description] [varchar](256) NULL,
[LibID] [int] NOT NULL,
[Status] [smallint] NULL,
[LogID] [int] NULL)
Ticket Attributes
CREATE TABLE [dbo].[TicketAttributes](
[TicketID] [int] NOT NULL,
[TicketAttrID] [int] IDENTITY(1,1) NOT NULL,
[AttributeID] [int] NOT NULL,
[AttributeGroup] [varchar](255) NULL,
[AttributeValue] [nvarchar](max) NULL,
[Status] [smallint] NULL,
[LogID] [int] NULL)
Where Ticket Attribute is another table that stores different attributes of a ticket like TicketStatus, TicketCategory etc..
Now I need to generate a report that looks like
TicketStatus1 TicketStatus 2 TicketStatus3
-----------------------------------------------------------------
TicketCategory1 7 3
Ticketcategory2 4
TicketCategory3 8
I want to see the count of each of the status of each ticket category.
For Eg:-
I have the following Data in TicketTable
----------------------------------------------
TicketID Name Price Date
------------------------------------------------
155 Ticket4 $20 16 Jan 2016
157 Ticket3 $300 17 Jan 2016
158 Ticket1 $100 18 Jan 2016
159 Ticket2 $500 19 Jan 2016
Now in the TicketAttribute Table
----------------------------------------------
TicketID AttributeID AttributeValue
------------------------------------------------
155 500 Joe
155 600 Reserved
155 700 Economy
155 800 San Jose
where AttributeIDs
500=Nameofthe Passenger
600= Status of Ticket
700= Class
800= Destination
Now lets say I want to see what is the count of number of active tickets in each of the class per status
Booked Cancelled PaymentPending ............
-----------------------------------------------------------------
Economy 7 3
Economy Plus 4
Business 8
Hope I am clear now.
how to go about this using SQL Query
USING PIVOT
;WITH cte AS (
SELECT
c.AttributeValue as Class
,s.AttributeValue as StatusOfTicket
FROM
Ticket t
LEFT JOIN TicketAttributes c
ON t.TicketId = c.TicketId
AND c.AttributeID = 700
LEFT JOIN TicketAttributes s
ON t.TicketId = s.TicketId
AND s.AttributeID = 600
)
SELECT *
FROM
cte
PIVOT (
COUNT(StatusOfTicket) FOR StatusOfTicket IN (Reserved,Cancelled,PaymentPending)
) p
USING Conditional Aggregation:
SELECT
c.AttributeValue as Class
,COUNT(DISTINCT CASE WHEN s.AttributeValue = 'Reserved' THEN c.TicketId END) as Reserved
,COUNT(DISTINCT CASE WHEN s.AttributeValue = 'Cancelled' THEN c.TicketId END) as Cancelled
,COUNT(DISTINCT CASE WHEN s.AttributeValue = 'PaymentPending' THEN c.TicketId END) as PaymentPending
FROM
Ticket t
LEFT JOIN TicketAttributes c
ON t.TicketId = c.TicketId
AND c.AttributeID = 700
LEFT JOIN TicketAttributes s
ON t.TicketId = s.TicketId
AND s.AttributeID = 600
GROUP BY
c.AttributeValue
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?
I have a table that holds availability status for workers. Here is the structure:
CREATE TABLE [dbo].[Availability]
(
[OID] BIGINT IDENTITY (1, 1) NOT NULL,
[LocumID] BIGINT NOT NULL,
[AvailableDate] SMALLDATETIME NOT NULL,
[AvailabilityStatusID] INT NOT NULL,
[LastModifiedAt] TIMESTAMP NOT NULL,
CONSTRAINT [PK_Availability] PRIMARY KEY CLUSTERED ([OID] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY];
And here is a result:
OID LocumID AvailableDate AvailabilityStatusID LastModifiedAt
-------------------- -------------------- ----------------------- -------------------- ------------------
1 1 2009-03-02 00:00:00 1 0x0000000000201A8C
2 2 2009-03-04 00:00:00 1 0x0000000000201A8D
3 1 2009-03-05 00:00:00 1 0x0000000000201A8E
4 1 2009-03-06 00:00:00 1 0x0000000000201A8F
5 2 2009-03-07 00:00:00 1 0x0000000000201A90
6 7 2009-03-09 00:00:00 1 0x0000000000201A91
7 1 2009-03-11 00:00:00 1 0x0000000000201A92
8 1 2009-03-12 00:00:00 2 0x0000000000201A93
9 1 2009-03-14 00:00:00 1 0x0000000000201A94
10 1 2009-03-16 00:00:00 1 0x0000000000201A95
Now, the table has over 3mil record and I noticed that there are inconsistencies in my data. I need to somehow find rows where for any [AvailableDate], the [LocumID] (regardless of how many,) must be unique. So, basically, a worker can have one of these [AvailabilityStatusID] = 1, 2, 3, or 4 on one date. However, in this table, there are errors where a worker is entered twice or more against a [AvailableDate] with same [AvailabilityStatusID] or different [AvailabilityStatusID]
How can I detect these records?
Regards.
WITH x AS
(
SELECT LocumID, dt = AvailableDate
FROM dbo.Availability
GROUP BY LocumID, AvailableDate
HAVING COUNT(*) > 1
)
SELECT a.OID, a.LocumID, a.AvailableDate,
a.AvailabilityStatusID, a.LastModifiedAt
FROM x
INNER JOIN dbo.Availability AS a
ON x.LocumID = a.LocumID
AND x.dt = a.AvailableDate
ORDER BY a.LocumID, a.AvailableDate;
Once you clean this data up (not sure what your rule will be regarding which rows to keep), you should consider a unique constraint on (LocumID, AvailableDate). Here is how you would create the constraint (though you will not be able to create it until you have removed the duplicates):
ALTER TABLE dbo.Availability
ADD CONSTRAINT uq_l_ad
UNIQUE (LocumID, AvailableDate);
Of course now you will have new errors returned to your application (Msg 2627), since your current code clearly doesn't already check if a LocumID/AvailabilityDate combination already exists before adding a new one.