Joining master and child with only the row count of child table - sql-server

Master Table:
CREATE TABLE [dbo].[db_Chat](
[ChatID] [int] IDENTITY(1,1) NOT NULL,
[MemberID] [int] NOT NULL,
[MsgText] [nvarchar](300) COLLATE Chinese_Taiwan_Stroke_CI_AS NOT NULL,
[DateCreated] [smalldatetime] NOT NULL,
CONSTRAINT [PK_db_Chat] PRIMARY KEY CLUSTERED
(
[ChatID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
Child Table:
CREATE TABLE [dbo].[db_Chat_Read](
[ChatReadID] [int] IDENTITY(1,1) NOT NULL,
[MemberID] [int] NOT NULL,
[ChatID] [int] NOT NULL,
[DateCreated] [smalldatetime] NOT NULL,
CONSTRAINT [PK_db_Chat_Read] PRIMARY KEY CLUSTERED
(
[ChatReadID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
I want to join the two tables and show the number of rows of child table, if there is no row selected then it can show zero.
I know the following code works fine.
select c.ChatID, COUNT(*) as ACount from db_chat c
left join db_Chat_Read r
on c.ChatID = r.ChatID
where c.ChatGroupID=2
GROUP BY c.ChatID
But I need something like this in a single SQL code, the following SQL code is invalid, but it is to show what result I want: I need full fields of master table with only Row Count of child table.
select c.*, COUNT(r.*) from db_chat c
left join db_Chat_Read r
on c.ChatID = r.ChatID
where c.ChatGroupID=2
Thank you~!

You can do what you want with a subquery:
select c.*,
(select COUNT(*) from db_Chat_Read r where c.ChatID = r.ChatId
) as ACount
from db_chat c
where c.ChatGroupID = 2;
However, I would recommend putting the columns in the group by.
EDIT:
You can also do this with an additional join:
select c.*, coalesce(ACount, 0) as ACount
from db_chat c left join
(select r.ChatId, count(*) as ACount
from db_Chat_Read r
group by r.ChatId
) r
on c.ChatID = r.ChatID
where c.ChatGroupID = 2;

Related

How to add fields from Items Table to Union all to table ItemOtherCode

Query is supposed to collect original items (from Items table) and alternative items (from ItemOtherCode table):
SELECT ItemCode,SelPrice1Default, ItemAraName, ItemLatName,
ItemNotes, UnitCode, ItemClassCode, ItemGroupCode,
ItemSubGroupCode, TaxSet, ExpireDate, ItemType, ItemEquation,
ItemDim, NotActive, UnitCode1
FROM dbo.Items
where ItemCode= 10003
union all
select OtherCode,BarcodeUnitPrice
from ItemOtherCode
where ItemCode= 10003
But when I make this union all of Items table and ItemOtherCode table, I get an error:
All queries combined using a UNION, INTERSECT or EXCEPT operator must have an
equal number of expressions in their target lists.
How to solve this problem?
DDL
Items Table
CREATE TABLE [dbo].[Items](
[ItemCode] [nvarchar](20) NOT NULL,
[ItemAraName] [nvarchar](100) NULL,
[ItemLatName] [nvarchar](100) NULL,
[ItemNotes] [nvarchar](100) NULL,
[UnitCode] [int] NOT NULL,
[ItemClassCode] [int] NULL,
[ItemGroupCode] [int] NULL,
[ItemSubGroupCode] [int] NULL,
[TaxSet] [float] NOT NULL,
[ExpireDate] [bit] NOT NULL,
[ItemType] [int] NULL,
[ItemEquation] [nvarchar](50) NULL,
[ItemDim] [int] NULL,
[NotActive] [bit] NOT NULL,
[UnitCode1] [int] NULL,
[BuyPriceDefault] [float] NOT NULL,
[PriceTypeCode] [int] NULL,
[SelPrice1Default] [float] NOT NULL,
[SelPrice2Default] [float] NOT NULL,
CONSTRAINT [PK_Items] PRIMARY KEY CLUSTERED
(
[ItemCode] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
ItemOtherCode Table
CREATE TABLE [dbo].[ItemOtherCode](
[ItemCode] [nvarchar](20) NOT NULL,
[OtherCode] [nvarchar](20) NOT NULL,
[BarcodeUnitPrice] [float] NULL,
CONSTRAINT [PK_ItemOtherCode] PRIMARY KEY CLUSTERED
(
[ItemCode] ASC,
[OtherCode] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Relation between them:
SELECT *
FROM dbo.Items i
INNER JOIN dbo.ItemOtherCode o ON o.ItemCode = i.ItemCode
UNION requires same count of fields and same columns name in both queries:
SELECT FIELD1, FIELD2, FIELD3 FIELD4
FROM TABLE1
UNION ALL
SELECT OTHER_FIELD1 AS FIELD1, OTHER_FIELD2 AS FIELD2, '' AS FIELD3, 0 AS FIELD4
FROM TABLE2
I surmise that [OtherCode] also is supposed to refer to parent Items table.
What you have tried:
SELECT OtherCode,BarcodeUnitPrice
FROM ItemOtherCode
WHERE ItemCode= 10003
or the same with join as shown in your question for FK explanation:
SELECT *
FROM dbo.Items i
INNER JOIN dbo.ItemOtherCode o ON o.ItemCode = i.ItemCode
WHERE i.ItemCode = 10003
Now you have found all the alternative items of 10003. But you need info about those items. Where is info about items stored? In Items table of course. So we need to go there once again but with different ItemCode values:
SELECT io.*
FROM dbo.Items i
INNER JOIN dbo.ItemOtherCode o ON o.ItemCode = i.ItemCode
INNER JOIN dbo.Items io on io.ItemCode = o.OtherCode --<<<
WHERE i.ItemCode = 10003
now we have all fields from Items table for all alternative items of 10003. Can do union now.
SELECT ItemCode,SelPrice1Default, ItemAraName, ItemLatName,
ItemNotes, UnitCode, ItemClassCode, ItemGroupCode,
ItemSubGroupCode, TaxSet, ExpireDate, ItemType, ItemEquation,
ItemDim, NotActive, UnitCode1
FROM dbo.Items i
WHERE i.ItemCode = 10003
UNION ALL
SELECT ItemCode,SelPrice1Default, ItemAraName, ItemLatName,
ItemNotes, UnitCode, ItemClassCode, ItemGroupCode,
ItemSubGroupCode, TaxSet, ExpireDate, ItemType, ItemEquation,
ItemDim, NotActive, UnitCode1
FROM dbo.ItemOtherCode o
INNER JOIN dbo.Items io on io.ItemCode = o.OtherCode
WHERE o.ItemCode = 10003
Don't know what is BarcodeUnitPrice supposed to mean so can't say what to do with it.

SQL JOIN not returning any results

I have a fairly simple join for three tables that is not returning any results. If I modify slightly I get sometime that appears jumbled (same data values in two fields). There is a Documents table from which I need the filename, a Variable table which has the variable definitions and names, and lastly I have a VariableValue table that the values for the variables are stored in. I am trying to get two values for each filename. Subject and Author. Here is the SQL call.
SELECT DISTINCT Documents.Filename, VariableValue.ValueText AS Author,
VariableValue.ValueText AS Subject
FROM Documents INNER JOIN
VariableValue ON Documents.DocumentID = VariableValue.DocumentID INNER JOIN
Variable AS VV1 ON VariableValue.VariableID = VV1.VariableID INNER JOIN
Variable AS VV2 ON VariableValue.VariableID = VV2.VariableID
WHERE (Documents.Filename LIKE N'ECO-%') AND (Documents.Deleted = 0) AND
(VV1.VariableName = N'Author') AND (VariableValue.ValueText <> '-') AND
(VV2.VariableName = N'Subject')
ORDER BY Author asc
If I remove the (VV2.VariableName = N'Subject') I get the following result:
Filename Author Subject
ECO-27533.docx billpark billpark
ECO-27630.docx billpark billpark
ECO-27683.docx billpark billpark
ECO-27790.docx billpark billpark
ECO-27812.docx billpark billpark
ECO-27975.docx billpark billpark
If I remove the (VV1.VariableName = N'Author') I get the following result:
Filename Author Subject
ECO-28720.docx 24006 LOW PWR BM DUMP 24006 LOW PWR BM DUMP
ECO-28595.docx 24052 PIN THREAD CORRECTION 24052 PIN THREAD CORRECTION
ECO-28517.docx 24087 24087
ECO-28791.docx 25584 REV A TO B 25584 REV A TO B
ECO-28714.docx 25873 PRESSURE RELEAF ASSY 25873 PRESSURE RELEAF ASSY
What I would like is:
Filename Author Subject
ECO-28720.docx billpark LOW PWR BM DUMP
Not sure what subtle thing I am missing?
TIA.
DDL
CREATE TABLE [dbo].[Variable](
[VariableID] [int] IDENTITY(1,1) NOT NULL,
[VariableName] [nvarchar](255) NOT NULL,
[VariableType] [int] NOT NULL,
[IsDeleted] [bit] NOT NULL,
[FlagUnique] [bit] NOT NULL,
[FlagMandatory] [bit] NOT NULL,
[FlagFreeUpdateAllVersion] [bit] NOT NULL CONSTRAINT [DF__Variable__FlagFr__05EEBAAE] DEFAULT ((0)),
[FlagFreeUpdateLatestVersion] [bit] NOT NULL CONSTRAINT [DF__Variable__FlagFr__06E2DEE7] DEFAULT ((0)),
CONSTRAINT [PK_Variable] PRIMARY KEY CLUSTERED
(
[VariableID] 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
CREATE TABLE [dbo].[VariableValue](
[VariableID] [int] NOT NULL,
[DocumentID] [int] NOT NULL,
[ProjectID] [int] NOT NULL,
[RevisionNo] [int] NOT NULL,
[ConfigurationID] [int] NOT NULL,
[ValueText] [nvarchar](max) NOT NULL CONSTRAINT [DF_VariableValue_ValueText] DEFAULT (N''),
[ValueInt] [int] NULL,
[ValueFloat] [float] NULL,
[ValueDate] [datetime] NULL,
[ValueCache] [nvarchar](64) NOT NULL CONSTRAINT [DF_VariableValue_ValueCache] DEFAULT (''),
[IsLongText] [bit] NOT NULL CONSTRAINT [DF_VariableValue_IsLongText] DEFAULT ((0)),
CONSTRAINT [PK_VariableValue] PRIMARY KEY CLUSTERED
(
[VariableID] ASC,
[DocumentID] ASC,
[ProjectID] ASC,
[RevisionNo] ASC,
[ConfigurationID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[VariableValue] WITH NOCHECK ADD CONSTRAINT [FK_VariableValue_DocumentConfiguration] FOREIGN KEY([ConfigurationID])
REFERENCES [dbo].[DocumentConfiguration] ([ConfigurationID])
GO
ALTER TABLE [dbo].[VariableValue] CHECK CONSTRAINT [FK_VariableValue_DocumentConfiguration]
GO
ALTER TABLE [dbo].[VariableValue] WITH NOCHECK ADD CONSTRAINT [FK_VariableValue_Documents] FOREIGN KEY([DocumentID])
REFERENCES [dbo].[Documents] ([DocumentID])
GO
ALTER TABLE [dbo].[VariableValue] CHECK CONSTRAINT [FK_VariableValue_Documents]
GO
ALTER TABLE [dbo].[VariableValue] WITH CHECK ADD CONSTRAINT [FK_VariableValue_Projects] FOREIGN KEY([ProjectID])
REFERENCES [dbo].[Projects] ([ProjectID])
GO
ALTER TABLE [dbo].[VariableValue] CHECK CONSTRAINT [FK_VariableValue_Projects]
GO
ALTER TABLE [dbo].[VariableValue] WITH NOCHECK ADD CONSTRAINT [FK_VariableValue_Variable] FOREIGN KEY([VariableID])
REFERENCES [dbo].[Variable] ([VariableID])
GO
ALTER TABLE [dbo].[VariableValue] CHECK CONSTRAINT [FK_VariableValue_Variable]
GO
VariableValue.ValueText AS Author,
VariableValue.ValueText AS Subject
You're calling the exact same alias.columnname and assigning it to both Author and Subject.
We need to add projectID, revisionNo and ConfigurationID to the results as the PK for VariableValue is a composite key. If we don't include them, then the results could get mixed up for different projects/revsions or configurations.
So unless you have a rule that says only return the latest revision for a document... we may get multiple records back.
SELECT Documents.Filename
, VariableValue.ProjectID
, VariableValue.RevisionNo
, VariableValue.ConfigurationID
, max(Case when VV1.VariableName = N'Author' then VariableValue.ValueText END) as Author
, max(Case when VV1.VariableName = N'Subject' then VariableValue.ValueText END) AS Subject
FROM Documents
INNER JOIN VariableValue
ON Documents.DocumentID = VariableValue.DocumentID
INNER JOIN Variable AS VV1
ON VariableValue.VariableID = VV1.VariableID
WHERE (Documents.Filename LIKE N'ECO-%')
AND (Documents.Deleted = 0)
AND (VariableValue.ValueText <> '-')
AND (VV1.VariableName in (N'Author',N'Subject')
GROUP BY Documents.Filename
, VariableValue.ProjectID
, VariableValue.RevisionNo
, VariableValue.ConfigurationID
ORDER BY Author asc
With a terrific assist from xQbert this code does it.
SELECT DISTINCT Documents.Filename, max(Case when VV1.VariableName = N'Author' then VariableValue.ValueText END) as Author,
max(Case when VV1.VariableName = N'Subject' then VariableValue.ValueText END) AS Description
FROM Documents INNER JOIN
VariableValue ON Documents.DocumentID = VariableValue.DocumentID INNER JOIN
Variable AS VV1 ON VariableValue.VariableID = VV1.VariableID INNER JOIN
Variable AS VV2 ON VariableValue.VariableID = VV2.VariableID
WHERE (Documents.Filename LIKE N'ECO-%') AND (Documents.Deleted = 0) AND (VV1.VariableName in (N'Author',N'Subject') AND (VariableValue.ValueText <> '-'))
Group BY Documents.Filename
ORDER BY Author asc

t-sql 2012 update foreign key value in primary table

In a special request run, I need to update Locker and Lock tables in a sql server 2012 database, I have the following 2 table definitiions:
CREATE TABLE [dbo].[Locker](
[lockerID] [int] IDENTITY(1,1) NOT NULL,
[schoolID] [int] NOT NULL,
[number] [varchar](10) NOT NULL,
[lockID] [int] NULL
CONSTRAINT [PK_Locker] PRIMARY KEY NONCLUSTERED
(
[lockerID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY =
OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 97)
ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[Lock](
[lockID] [int] IDENTITY(1,1) NOT NULL,
[schoolID] [int] NOT NULL,
[comboSeq] [tinyint] NOT NULL
CONSTRAINT [PK_Lock] PRIMARY KEY NONCLUSTERED
(
[lockID] ASC
)
The Locker table is the main table and the Lock table is the secondary table. I need to add 500 new Locker numbers that the user has given to me to place in the Locker table and is uniquely defined by LockerID. I also need to add 500 new rows to the correspsonding Lock table that is uniquely defined in the Lock table and identified by the lockid.
Since lockid is a key value in the lock table and is uniquely defined in the locker table, I would like to know how to update the lock table with the 500 new rows. I would then like to take value of lockid (from lock table for the 500 new rows that were created) and uniquely place those 500 lockids uniquely into the 500 rows that were created for the lock table.
I have sql that looks like the following so far:
declare #SchoolID int = 999
insert into test.dbo.Locker ( [schoolID], [number])
select distinct LKR.schoolID, A.lockerNumber
FROM [InputTable] A
JOIN test.dbo.School SCH
ON A.schoolnumber = SCH.type
and A.schoolnumber = #SchoolNumber
JOIN test.dbo.Locker LKR
ON SCH.schoolID = LKR.schoolID
AND A.lockerNumber not in (select number
from dbo.Locker
where schoolID = #SchoolID)
order by LKR.schoolID, A.lockerNumber
I am not certain how to complete the rest of the task of placing lockerid uniquely into lock and locker tables? Thus can you either modify the sql that I just listed above and/or
come up with some new sql that will show me how to accomplish my goal?
You should use OUTPUT statement. First you should add rows to the lock table then gram lockid and prepare insert to locker table. This shoul meet your expectations:
DECLARE #tmp TABLE (lockid INT)
INSERT dbo.Lock
( schoolID, comboSeq )
OUTPUT Inserted.lockID INTO #tmp ( lockid )
(SELECT
999,
1
FROM master..spt_values sv WHERE sv.type = 'P' AND sv.number <= 500);
INSERT INTO dbo.Locker( schoolID, number, lockID )
SELECT x.schoolID, x.lockerNumber, y.lockid
FROM
(
SELECT TOP 100 PERCENT DISCTINCT LKR.schoolID, A.lockerNumber, ROW_NUMBER() OVER (ORDER BY A.LockerNumber) rn
FROM [InputTable] A
JOIN test.dbo.School SCH ON A.schoolnumber = SCH.type
and A.schoolnumber = #SchoolNumber
JOIN test.dbo.Locker LKR ON SCH.schoolID = LKR.schoolID
AND A.lockerNumber not in (select number from dbo.Locker
WHERE schoolID = #SchoolID)
ORDER by LKR.schoolID, A.lockerNumber ) x
JOIN (SELECT t.lockid, ROW_NUMBER() OVER (ORDER BY t.lockid) AS rn FROM #tmp t
) y
ON x.rn = y.rn

Select distinct from a table foreach value from another

I have a view that gets the status for each category for each venue.
I need to display all of the above, but only the latest in the status for each category for each venue.
A general idea could be, for each item in a shop, there is a status. So for the cucumbers, letters, and tomatoes in shop A, the status would be good good bad respectively. Same for shop B, C etc. Now these statuses can be updated at any time - but only the latest update should be displayed.
I have tried to create a view such as the following:
SELECT
dbo.vwVenues.HospitalID
, dbo.vwVenues.Hospital AS hospitalName
, dbo.tblVenueStatus.id AS statusId
, dbo.tblVenueStatusName.statusName
, dbo.tblVenueStatusCategories.categoryName
, dbo.tblVenueStatus.dateAdded AS statusDateAdded
, dbo.tblVenueStatus.loggedBy AS statusLoggedBy
, dbo.tblVenueStatusNotes.noteContent
, dbo.tblVenueStatusNotes.dateAdded AS noteDateAdded
, dbo.tblVenueStatusNotes.removed AS noteRemoved
, dbo.tblVenueStatus.resolved AS statusResolved
, dbo.tblVenueStatus.resolveDate AS statusResolveDate
, dbo.tblVenueStatus.removed AS statusRemoved
, dbo.tblVenueStatus.dateRemoved AS statusRemovedDate
, dbo.tblVenueStatus.categoryId
FROM
dbo.tblVenueStatusName
RIGHT OUTER JOIN dbo.tblVenueStatusNotes
RIGHT OUTER JOIN dbo.tblVenueStatus
ON dbo.tblVenueStatusNotes.id = dbo.tblVenueStatus.noteId
LEFT OUTER JOIN dbo.tblVenueStatusCategories
ON dbo.tblVenueStatus.categoryId = dbo.tblVenueStatusCategories.id
ON dbo.tblVenueStatusName.id = dbo.tblVenueStatus.statusNameId
FULL OUTER JOIN dbo.vwVenues
ON dbo.tblVenueStatus.venuId = dbo.vwVenues.HospitalID
I get all the venues, but I get all the statuses for each category, instead of just the latest. Even trying to phrase the question right was hard - so my current search yeilded nothing.
Could anyone please help?
Edit
CREATE TABLE [dbo].[tblVenueStatus](
[id] [int] IDENTITY(1,1) NOT NULL,
[statusNameId] [int] NOT NULL,
[venuId] [int] NOT NULL,
[categoryId] [int] NOT NULL,
[loggedBy] [nvarchar](50) NOT NULL,
[noteId] [int] NOT NULL,
[dateAdded] [datetime] NOT NULL,
[resolved] [tinyint] NOT NULL,
[resolveDate] [datetime] NULL,
[removed] [tinyint] NOT NULL,
[dateRemoved] [datetime] NULL,
CONSTRAINT [PK_tblSiteVenuStatus] PRIMARY KEY CLUSTERED
CREATE TABLE [dbo].[tblVenueStatusCategories](
[id] [int] IDENTITY(1,1) NOT NULL,
[categoryName] [nvarchar](50) NOT NULL,
[categoryDescription] [nvarchar](150) NULL,
CONSTRAINT [PK_tblVenueStatusCategories] PRIMARY KEY CLUSTERED
CREATE TABLE [dbo].[tblVenueStatusName](
[id] [int] IDENTITY(1,1) NOT NULL,
[statusName] [nvarchar](50) NOT NULL,
[statusDescription] [nchar](150) NULL,
CONSTRAINT [PK_tblVenuStatusName] PRIMARY KEY CLUSTERED
CREATE TABLE [dbo].[tblVenueStatusNotes](
[id] [int] IDENTITY(1,1) NOT NULL,
[noteContent] [nvarchar](500) NULL,
[dateAdded] [datetime] NOT NULL,
[removed] [tinyint] NOT NULL,
[dateRemoved] [datetime] NULL,
CONSTRAINT [PK_tblVenueStatusNotes] PRIMARY KEY CLUSTERED
Desired Output will be:
Venu Name | Category 1 Status | Category 2 Status
Try something like this. It's largely sumerised because of what you said you needed to see in your sample. Try this and let me know if it helps. You can add other columns you need here, It is taking the latest status in each category for each hospital, and showing all hospitals and categories in a easily readable grid.
SELECT * FROM
(
SELECT VwV.HospitalID, vwV.Hospital, ISNULL(vStatusName.statusName, '') AS statusName, vStatusCategories.categoryName
FROM
(
SELECT topStatus.HospitalID, topStatus.categoryId, sta2.id
FROM
(
SELECT HospitalID, categoryId, MAX(sta.dateAdded) AS DateAdded
FROM vwVenues
INNER JOIN tblVenueStatus sta ON sta.venuId = dbo.vwVenues.HospitalID
GROUP BY HospitalID, categoryId
) topStatus
INNER JOIN tblVenueStatus sta2 ON sta2.dateAdded = topStatus.DateAdded AND sta2.venuId = topStatus.HospitalID) Statuss
FULL OUTER JOIN vwVenues vwV ON vwV.HospitalID = Statuss.HospitalID
LEFT OUTER JOIN tblVenueStatus vStatus ON vStatus.id = Statuss.id
LEFT OUTER JOIN tblVenueStatusName vStatusName ON vStatusName.id = vStatus.statusNameId
LEFT OUTER JOIN tblVenueStatusCategories vStatusCategories ON vStatus.categoryId = vStatusCategories.id AND vStatusName.id = vStatus.statusNameId
LEFT OUTER JOIN tblVenueStatusNotes vStatusNotes ON vStatusNotes.id = vStatus.noteId
) AS s
PIVOT (MIN(StatusName)
FOR [CategoryName] IN ([Category1],[Category2])) AS PTbl

Order by performance issue on joined tables

I have two tables: "Alarms" and "Devices" and a view "vwAlarms"
Alarms Table has 250K rows and Devices Table has only 50 rows.
vwAlarms is just join of two table.
my problems is when i add Top(x) and order by id desc to select * from vwAlarm it takes 10 seconds to execute query. however the same query runs quickly on table Alarm.
select * from Alarm --in milliseconds.
select * from vwAlarms --in milliseconds
select top (100) * from Alarms order by id desc --in milliseconds
select top (100) * from vwAlarms order by id desc --takes 10 seconds
Here is my view definition:
CREATE VIEW [dbo].[vwAlarms]
AS SELECT
dbo.Devices.Id ,
dbo.Devices.Name ,
dbo.Devices.PortsTagPrefix ,
dbo.Devices.ControlCenterNumber ,
dbo.Devices.AlarmNumber1 ,
dbo.Devices.AlarmNumber2 ,
dbo.Devices.SimCardNumber ,
dbo.Devices.StationNumber ,
dbo.Devices.SlaveId ,
dbo.Devices.TypeId ,
dbo.Devices.RegionId ,
dbo.Devices.EnquiryPassword ,
dbo.Devices.SetupPassword ,
dbo.Devices.ProtocolId ,
dbo.Devices.UploadedPacketsCount ,
dbo.Devices.LastPort ,
dbo.Devices.LastIp ,
dbo.Devices.IsForTesting ,
dbo.Devices.Latitude ,
dbo.Devices.Longitude ,
dbo.Devices.X ,
dbo.Devices.Y ,
dbo.Devices.MainSchematicId ,
dbo.Devices.MainTimeChartId ,
dbo.Devices.MainCategoryChartId ,
dbo.Alarms.Id ,
dbo.Alarms.DeviceId ,
dbo.Alarms.LogId ,
dbo.Alarms.PortId ,
dbo.Alarms.TypeId ,
dbo.Alarms.DateTime ,
dbo.Alarms.AcknowledgerId ,
dbo.Alarms.AcknowledgeDateTime ,
dbo.Alarms.Acknowledged ,
dbo.Alarms.PortValue
FROM Devices
INNER JOIN Alarms ON Devices.Id = Alarms.DeviceId
ORDER BY dbo.Alarms.Id DESC
Here is execution plan:
Warning message of of sort:
Alarm table Schema:
CREATE TABLE [dbo].[Alarms](
[Id] [int] IDENTITY(1,1) NOT NULL,
[DeviceId] [int] NOT NULL,
[LogId] [int] NOT NULL,
[PortId] [int] NOT NULL,
[TypeId] [int] NOT NULL,
[DateTime] [datetime2](0) NOT NULL,
[AcknowledgerId] [int] NULL,
[AcknowledgeDateTime] [datetime2](0) NULL,
[Acknowledged] [bit] NULL,
[PortValue] [numeric](19, 4) NULL,
CONSTRAINT [PK_Alarms] 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
ALTER TABLE [dbo].[Alarms] WITH CHECK ADD CONSTRAINT [FK_Alarms_AlarmTypes] FOREIGN KEY([TypeId])
REFERENCES [dbo].[AlarmTypes] ([Id])
GO
ALTER TABLE [dbo].[Alarms] CHECK CONSTRAINT [FK_Alarms_AlarmTypes]
GO
ALTER TABLE [dbo].[Alarms] WITH CHECK ADD CONSTRAINT [FK_Alarms_Devices] FOREIGN KEY([DeviceId])
REFERENCES [dbo].[Devices] ([Id])
GO
ALTER TABLE [dbo].[Alarms] CHECK CONSTRAINT [FK_Alarms_Devices]
GO
ALTER TABLE [dbo].[Alarms] WITH CHECK ADD CONSTRAINT [FK_Alarms_ExtendedUsers] FOREIGN KEY([AcknowledgerId])
REFERENCES [dbo].[ExtendedUsers] ([Id])
GO
ALTER TABLE [dbo].[Alarms] CHECK CONSTRAINT [FK_Alarms_ExtendedUsers]
GO
ALTER TABLE [dbo].[Alarms] WITH CHECK ADD CONSTRAINT [FK_Alarms_Logs] FOREIGN KEY([LogId])
REFERENCES [dbo].[Logs] ([Id])
ON UPDATE CASCADE
ON DELETE CASCADE
GO
ALTER
Is using the view a must? If not, you probably should first get your 100 alarms and then join with devices. That's the end result you want right?
It is not being smart about the sort.
Do you have the FK defined?
I would try all option of the table hints on the join to try and push that sort earlier.
Join Hints (Transact-SQL)
If table hints does not work I would try a Cross Apply.
I think the Cross Apply should be smart about sort.
But at the cost of not being as fast on the "join".
So it would be good at returning the first 1000 or 10,000 but bad at all.
SELECT t1.*, t2o.*
FROM t1
CROSS APPLY
(
SELECT *
FROM t2
WHERE t2.t1_id = t1.id
) t2o

Resources