I have an indexed view but when I run queries on that view the index which is built on View is not applied and the query runs without index. Below is my dummy script:
Tables + View+ Index on View
CREATE TABLE P_Test
(
[PID] INT IDENTITY,
[TID] INT,
[StatusID] INT
)
CREATE TABLE T_Test
(
[TID] INT IDENTITY,
[FID] INT,
)
CREATE TABLE F_Test
(
[FID] INT IDENTITY,
[StatusID] INT
)
GO
INSERT INTO F_Test
SELECT TOP 1000 ABS(CAST(NEWID() AS BINARY(6)) %10) --below 100
FROM master..spt_values
INSERT INTO T_Test
SELECT TOP 10000 ABS(CAST(NEWID() AS BINARY(6)) %1000) --below 1000
FROM master..spt_values,
master..spt_values v2
INSERT INTO P_Test
SELECT TOP 100000 ABS(CAST(NEWID() AS BINARY(6)) %10000) --below 10000
,
ABS(CAST(NEWID() AS BINARY(6)) %10)--below 10
FROM master..spt_values,
master..spt_values v2
GO
CREATE VIEW [TestView]
WITH SCHEMABINDING
AS
SELECT P.StatusID AS PStatusID,
F.StatusID AS FStatusID,
P.PID
FROM dbo.P_Test P
INNER JOIN dbo.T_Test T
ON T.TID = P.TID
INNER JOIN dbo.F_Test F
ON T.FID = F.FID
GO
CREATE UNIQUE CLUSTERED INDEX [PK_TestView]
ON [dbo].[TestView] ( [PStatusID] ASC, [FStatusID] ASC, [PID] 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
Now when I run the following queries the [PK_TestView] index is not being applied:
SELECT PStatusID ,
FStatusID ,
PID FROM [TestView]
SELECT PStatusID ,
FStatusID ,
PID FROM [TestView]
WHERE [PStatusID]=1
SELECT COUNT(PStatusID) FROM [TestView]
WHERE [PStatusID]=1
Can you help me fixing this?
You need to use the NOEXPAND hint. SQL Server will not consider matching indexed views without this (even if the view name is referenced in the query) unless you are on Enterprise Edition engine.
SELECT COUNT(PStatusID)
FROM [TestView]
WITH (NOEXPAND) -- this line
WHERE [PStatusID]=1
This should give you the first, much cheaper, plan
Related
i have a temporary table which i need to update, the first row is updated but the second row updates as null , please help
declare #T Table
(
ID int,
Name nvarchar(20),
rownum int
)
insert into #T(ID,rownum)
select ID, rownum = ROW_NUMBER() OVER(order by id) from testtabel4
select * from testtabel4
update #t
set Name=case when rownum>1 then (select top 1 Name from #T x where x.rownum=(y.rownum-1))
else 'first' end
from #t y
select * from #T
and here the definition of testtabel4
CREATE TABLE [dbo].[testtabel4](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](80) NULL,
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
and here is the output
ID Name
1 first
2 NULL
I think your update would be better written with lag() and an updateable CTE.
with cte as (
select name, lag(name, 1, 'first') over(order by rownum) lag_name
from #t
)
update cte set name = lag_name
With this technique at hand, it is plain to see that don't actually need to feed the table first, then insert into it. You can do both at once, like so:
insert into #t (id, name, rownum)
select
id,
lag(name, 1, 'first') over(order by id),
row_number() over(order by id)
from testtabel4
I am not sure that you even need rownum column anymore, unless it is needed for some other purpose.
You are only inserting two columns in the #T:
insert into #T (ID, rownum)
select ID, ROW_NUMBER() OVER (order by id)
from testtabel4;
You are not inserting name so it is NULL on all rows. Hence, the then part of the case expression will always be NULL.
I have built a function/query that returns [paths] as an adjacency list as shown below and it works well,
however I am trying t figure out how to limit the results to only complete paths, not each iteration of depth(n).
for example, out of the following results shown below, only these are valid, FULL paths I want to return or filter:
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close>B-GARNER>VB1>VB1_Close
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close>B-GARNER>VB1>VB1_Open
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close>B-GARNER>VB1>VB2
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Open>V30A2>V30A2_Close>S2-UG
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Open>V30A2>V30A2_Open>S1-UG
TEST
select * from fn_Get_SubTreePaths('S11', 11) order by DPath
results
S11>S11-LG
S11>S11-LG>V613
S11>S11-LG>V613>V613_Close
S11>S11-LG>V613>V613_Close>B31A
S11>S11-LG>V613>V613_Close>B31A>B30
S11>S11-LG>V613>V613_Close>B31A>B30>B30A
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close>B-GARNER
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close>B-GARNER>VB1
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close>B-GARNER>VB1>VB1_Close
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close>B-GARNER>VB1>VB1_Open
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close>B-GARNER>VB1>VB2
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Open
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Open>V30A2
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Open>V30A2>V30A2_Close
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Open>V30A2>V30A2_Close>S2-UG
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Open>V30A2>V30A2_Open
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Open>V30A2>V30A2_Open>S1-UG
In other words - how can I determine only those paths which include the bottom-most item in the traversal / depth ?
Any help would be greatly appreciated! I am reading through Joe Celko's Trees and Hierarchies in SQL for Smarties - but have so far not wrapped my brain around how to limit the results correctly ...
Here is the code;
create function [dbo].[fn_Get_SubTreePaths]( #Start varchar(20), #MaxLevels int)
returns table
as
RETURN(
WITH CTE AS (
SELECT
c.DeviceID AS Start,
CAST(d.DeviceName AS VARCHAR(2000)) AS Path,
c.ConnectedDeviceID,
1 AS Level
FROM Connections c
INNER JOIN Devices d ON d.ID=c.DeviceID
AND d.DeviceName=#Start
UNION ALL
SELECT
r.Start,
CAST(r.Path + '>'+ d.DeviceName AS VARCHAR(2000)),
--CAST(r.Path + ' -> ' + d.DeviceName + '-' + cast(r.SLevel as varchar) AS VARCHAR),
c.ConnectedDeviceID,
Level = r.Level + 1
FROM Connections c
INNER JOIN Devices d ON d.ID=c.DeviceID
and d.DeviceName<>#Start
INNER JOIN CTE r ON c.DeviceID=r.ConnectedDeviceID
AND r.Level < #MaxLevels
)
SELECT
DISTINCT a.DPath
FROM (
SELECT c.Path + '>' + ISNULL(d.DeviceName,'?') AS DPath
FROM CTE c
INNER JOIN Devices d ON d.ID=c.ConnectedDeviceID
) a
)
CREATE TABLE [dbo].[Connections](
[ID] [int] NOT NULL,
[DeviceID] [int] NULL,
[ConnectedDeviceID] [int] NULL
CONSTRAINT [PK_Connections] 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]
CREATE TABLE [dbo].[Devices](
[ID] [int] IDENTITY(1,1) NOT NULL,
[DeviceName] [varchar](50) NULL
CONSTRAINT [PK_Devices] 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]
And the view used by the CTE:
ALTER view [dbo].[vw_DeviceConnections] as
select
c.ID as ID,
c.DeviceID as DeviceID,
d.DeviceName,
d.DeviceType,
c.ConnectedDeviceID as ConnDeviceID,
cd.DeviceName as ConnDeviceName,
cd.DeviceType as ConnDeviceType
from Devices d
inner join Connections c on
d.id=c.DeviceID
inner join Devices cd on
cd.id=c.ConnectedDeviceID
See the answer working on SQLFiddle here.
Here's my strategy: Make your recursive CTE return the current node as well as the node before it. Then you can join that on itself and restrict it to rows whose last node is not used as the parent of another node.
Here's how your function would look (replace 'S11' and 15 with your parameters):
WITH CTE AS (
SELECT
d.ID AS Start,
CAST(d.DeviceName AS VARCHAR(2000)) AS Path,
d.ID AS node,
NULL AS parent,
1 AS Level
FROM Devices d
WHERE d.DeviceName= 'S11'
UNION ALL
SELECT
r.Start,
CAST(r.Path + '>'+ d.DeviceName AS VARCHAR(2000)),
d.ID as node,
r.node as parent,
r.Level + 1 as Level
FROM CTE r
INNER JOIN Connections c ON c.DeviceID = r.node
INNER JOIN Devices d ON d.ID = c.ConnectedDeviceID
WHERE r.Level < 15
),
Trimmed as (
SELECT L.*
FROM CTE L
LEFT JOIN CTE R on L.node = R.parent
WHERE R.parent IS NULL
)
SELECT * FROM Trimmed
Let me know if you'd like clarification about how it works, I can try to explain it better.
try next solution
select
*
from
fn_Get_SubTreePaths('S11', 11) f1
where
(
select
count(*)
from
fn_Get_SubTreePaths('S11', 11) f2
where
charindex(f1.dPath, f2.dPath) > 0
) = 1
order by DPath
I'm getting following error if i'm running a stored-procedure
Cannot insert duplicate key row in object ‘dbo.tabTac’ with unique index
‘IX_tabTac’.
Unique Index IX_tabTac:
CREATE UNIQUE NONCLUSTERED INDEX [IX_tabTac] ON [dbo].[tabTAC]
(
[TAC] 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, FILLFACTOR = 90) ON [PRIMARY]
Table tabTac:
CREATE TABLE [dbo].[tabTAC](
[idTAC] [int] IDENTITY(1,1) NOT NULL,
[TAC] [char](8) NOT NULL,
[fiModel] [int] NOT NULL,
CONSTRAINT [PK_tabTac] PRIMARY KEY CLUSTERED
(
[idTAC] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[tabTAC] WITH NOCHECK ADD CONSTRAINT [FK_tabTac_modModel] FOREIGN KEY([fiModel])
REFERENCES [dbo].[modModel] ([idModel])
GO
ALTER TABLE [dbo].[tabTAC] CHECK CONSTRAINT [FK_tabTac_modModel]
Stored-Procedure InsertTacsFromClaims:
CREATE PROC [dbo].[InsertTacsFromClaims]
with execute as Owner
AS
BEGIN
INSERT INTO tabTac
select substring(t.SSN_Number,1,8)as TAC
,m.idModel as fiModel
from tabData t
inner join modmodel m
ON t.fimodel=m.idmodel
WHERE t.fiproducttype=1
and m.idModel>1
and not exists(
select fiModel from tabTac WHERE TAC=substring(t.SSN_Number,1,8)
)
GROUP BY substring(t.SSN_Number,1,8),m.idModel
END
RETURN ##ROWCOUNT;
I would have thought that i have prevented this error with:
and not exists(
select fiModel from tabTac WHERE TAC=substring(t.SSN_Number,1,8)
)
Edit:
#Sparkys second query(ModelName added) returns following:
TAC fiModel ModelName
01233300 777 U5A
01238300 771 W20I
01238300 784 W20
35427603 720 C903
35773704 781 E15I
35905104 451 W595
35946804 793 W150I
35959004 813 ST18I
Now it's obvious that one TAC in tabData(first 8 chars of SSN_Number)has not a distinct model but can be linked to multiple models. That causes the error because in tabTac the TAC must be unique.
Thanks in advance
Check to see if the SSN_Number is duplicated in the TabData table or if the join to modModel produces duplicates.
select substring(t.SSN_Number,8),count(*)
from tabData
groub by substring(t.SSN_Number,8)
having count(*) > 1
and
select substring(t.SSN_Number,1,8)as TAC
,m.idModel as fiModel
from tabData t
inner join modmodel m
ON t.fimodel=m.idmodel
WHERE t.fiproducttype=1
and m.idModel>1
GROUP BY substring(t.SSN_Number,1,8),m.idModel
HAVING count(*) > 1
If either query returns duplicate SSN_numbers, you'll get the error. Your code only checks that you don't add a SSN number from the table if it already exists in the table you are trying to populate, but doesn't account for potential dupes in the data already
There are several approaches to only get the most recent fiModel code, here is one example:
select SSN_Number,fiModel
FROM tabData td1
JOIN
( -- This will get the latest date from tabData
select SSN_Number,max(received_date) as TheLastestDate
FROM tabData td
LEFT JOIN tabTac tc on tc.TAC = substring(TD.SSN_number,1,8)
and tc.fiModel = td.fModel
WHERE tc.fiModel is NULL
GROUP BY SSN_Number) xx ON xx.SSN_number=td1.ssn_number
and td1.received_date = xx.TheLatestDate
Note that if you have multiple fiModels on the same date, the above won't handle it because it does not know how to distinguish between which to use. Hope this points you in the right direction.
I have files that are versioned, using a major and minor version.
Now what I want to do is grab the highest major versions of all the files (which I've done already). Then check minor versions to see if it contains a 0. If it does throw out that whole set of versions. So for instance. My first query returns:
FileA Ver 1.0, 1.1, 1.2, 1.3
FileB Ver 2.1, 2.2, 2.3, 2.4
FileC Ver 5.1, 5.2, 5.3.
So in all 11 rows/records. Now my second query should take that result and throw out all of the FileA versions because one of the versions has a 0 as a minor version. So the second query should return:
FileB Ver 2.1, 2.2, 2.3, 2.4
FileC Ver 5.1, 5.2, 5.3.
7 rows/records in all.
Could anyone help me with this query? I'm using SQL server 2008 if that helps.
latestFileMajorVersion(fileId, majRev)
as
(
--Gets files with highest major version
select distinct v_fileid, max(v_majrev)
from files
group by v_fileid
),
latestFileVersions(fileId, majRev, minRev)
as
(
--Gets files with highest major version and all minor versions
select fileId, majRev, minrev
from files
inner join latestFileMajorVersion on files.v_fileid = latestFileMajorVersion.fileId
where v_majrev = latestFileMajorVersion.majRev
),
latestFileVersion(fileId, majRev, minRev)
as
(
select fileId, majRev, minrev
from latestFileVersions
where --I'm stuck here.
)
select v_fileid, v_majrev, v_minrev, v_status
from files
inner join latestFileVersion on v_fileid = latestFileVersion.fileId
where latestFileVersion.majRev = v_majrev and latestFileVersion.minRev = v_minrev and v_status = 'UnAssigned'
The table:
CREATE TABLE [dbo].[Files](
[v_fileid] [uniqueidentifier] NOT NULL,
[v_libid] [uniqueidentifier] NOT NULL,
[v_userid] [uniqueidentifier] NOT NULL,
[v_majrev] [int] NOT NULL,
[v_minrev] [int] NOT NULL,
[v_status] [nvarchar](16) NOT NULL,
CONSTRAINT [PK_Files] PRIMARY KEY CLUSTERED
(
[v_fileid] ASC,
[v_majrev] ASC,
[v_minrev] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [IX_Files] UNIQUE NONCLUSTERED
(
[v_majrev] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [IX_Files] UNIQUE NONCLUSTERED
(
[v_minrev] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
You can rewrite your query using this idea for the syntax. It works for the sample data you provided.
declare #t table (fileid varchar(10), [version] varchar(5))
insert #t values('FileA','1.0')
insert #t values('FileA','1.1')
insert #t values('FileA','1.2')
insert #t values('FileA','1.3')
insert #t values('FileB','2.1')
insert #t values('FileB','2.2')
insert #t values('FileB','2.3')
insert #t values('FileB','2.4')
insert #t values('FileC','5.1')
insert #t values('FileC','5.2')
insert #t values('FileC','5.3')
SELECT * FROM #t t
WHERE NOT EXISTS
(SELECT 1 FROM #t WHERE PARSENAME([version], 1) = '0' AND t.fileid = fileid)
Here's a working solution with a little example table you can play with. Execute it exactly as it is with no other sql around it. You can then modify it to suit your need.
declare #files table (fileId int identity(1,1), majRev int, minRev int)
insert into #files values (1,0)
insert into #files values (1,1)
insert into #files values (2,0)
insert into #files values (2,2)
insert into #files values (3,1)
insert into #files values (3,2)
insert into #files values (4,1)
select fileId, majRev, minRev
from #files f
where not exists
(select *
from #files
where minRev = 0
and majRev = f.majRev
and fileId = f.fileId)
When I try to code something, sometimes I get too caught up with making an "elegant" solution, as opposed to just brute-forcing the solution and having it apply only to a particular problem.
So I'm wondering if 1) I've brute-forced this solution and 2) is there a more "elegant" or clever way to go about writing this. All comments, critiques, and/or suggestions are welcome.
;with solution(fileId, majrev, minrev, status, checkid, maxrev)
as
(
select *
from files
inner join
(
select v_fileid, MAX(v_majrev) as max_major
from files
group by v_fileid
) as max_versions on max_versions.v_fileid = files.v_fileid and
max_major = files.v_majrev
except
select *
from files
inner join
(
select v_fileid, MAX(v_majrev) as max_major
from files
where v_minrev = 0
group by v_fileid
) as unwanted_versions on unwanted_versions.v_fileid = files.v_fileid and
max_major = files.v_majrev
)
select *
from solution
A wrong percentage has been applied to a field (TotalPercentageAmount) and I need to correct it.
Given 2 fields Amount and TotalPercentageAmount how can I calculate what percentage was applied?
I need to work out percentage applied to TotalPercentageAmount and UPDATE the column with correct percentage.
Little script I have created to mimin my scenario . Table created contains wrong TotalPercentageAmount!!!
CREATE TABLE [dbo].[SalesReportTest](
[Id] [int] NOT NULL,
[Amount] [decimal](18, 4) NOT NULL,
[TotalPercentageAmount] [decimal](18, 4) NOT NULL,
CONSTRAINT [PK_SalesReportTest] 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
SET NOCOUNT ON;
SET XACT_ABORT ON;
GO
BEGIN TRANSACTION;
INSERT INTO [dbo].[SalesReportTest]([Id], [Amount], [TotalPercentageAmount])
SELECT 1, 55.0000, 52.0300 UNION ALL
SELECT 2, 440.0000, 416.2200 UNION ALL
SELECT 3, 300.0000, 283.8000 UNION ALL
SELECT 4, -55.0000, -52.0300 UNION ALL
SELECT 5, 98.0000, 92.7000 UNION ALL
SELECT 6, -10.0000, -9.4600
COMMIT;
RAISERROR (N'[dbo].[SalesReportTest]: Insert Batch: 1.....Done!', 10, 1) WITH NOWAIT;
GO
You can try the following to determine the percentage used. However, it appears that there is some precision loss because the Amount column appears to be truncated. I also include a column that demonstrates the 5.72% calc in your sample data.
SELECT *,
CAST ((Amount / TotalPercentageAmount - 1) * 100 AS DECIMAL (5, 2)) as Pct,
CAST (TotalPercentageAmount * 1.0572 AS INT) Amt_Calc
FROM [SalesReportTest]
If I understand you correctly, you can update the percentages like:
update SalesReportTest
set TotalPercentageAmount = Amount /
(select sum(amount) from SalesReportTest)