Update statement in terms of performance - sql

May be a discussion point, but would like to know which one is good in terms of performance.
update by join
update by where clause
basic data are:
Set Nocount On;
Declare #students Table
(
Id Int Identity(1,1)
,Name Varchar(100)
,CurrentRank Int
)
Insert Into #students(Name) Values
('Alex')
,('Peter')
,('Mark')
,('Jhon')
,('Steven')
Declare #results Table
(
RowId Int Identity(1,1)
,Id Int
,Marks Int
,ResultYear Int
)
Insert Into #results(Id,Marks,ResultYear) Values
(1,32,2015)
,(1,29,2015)
,(1,31,2015)
,(2,40,2015)
,(2,39,2015)
,(2,38,2015)
,(3,18,2015)
,(3,25,2015)
,(3,29,2015)
,(4,35,2015)
,(4,31,2015)
,(4,21,2015)
,(5,25,2015)
,(5,33,2015)
,(5,40,2015)
---- 1. update with join
Update s
Set s.CurrentRank = r.ResultRank
From #students As s
Join
(
Select Id
,Row_Number() Over(Order By Avg(r.Marks) Desc) As ResultRank
From #results As r
Where r.ResultYear = 2015
Group By r.Id
) As r On s.Id = r.Id
---- 2. update by where
Update #students
Set CurrentRank = r.ResultRank
From (
Select Id As StudentId
,Row_Number() Over(Order By Avg(r.Marks) Desc) As ResultRank
From #results As r
Where r.ResultYear = 2015
Group By r.Id
) As r
Where Id = r.StudentId
in above both update statement, would like to know, which one is
good/best in terms of performance....
considered there would be large amount of data, like records are in
lacks in both, master and child table.

Related

How do I limit the number of ROWS being returned in my SQL Server Function?

CREATE FUNCTION [dbo].[GET_CUSTOMER_DATA]
(
-- Add the parameters for the function here
#customerID bigint,
#maxRows int,
#offset int,
#rows int
)
RETURNS TABLE
AS
RETURN
(
-- Add the SELECT statement with parameter references here
SELECT *
FROM SS_CustomerCard AS SS_CC
INNER JOIN SS_PersonalRepresentative AS SS_PR
ON SS_PR.customerID = SS_CC.ID
INNER JOIN SS_ApplicationStatus AS SS_AS
ON SS_AS.CustomerID = SS_CC.ID
WHERE
SS_CC.ID='#customerID'
ORDER BY SS_AS.EventDateTime DESC, SS_CC.FirstName DESC, SS_CC.LastName DESC
OFFSET #offset ROWS
FETCH NEXT #rows ROWS ONLY
)
GO
I want to pass a parameter to limit the number of rows being returned (maxRows)
You can use the TOP() clause in the SELECT statement.
Here is a conceptual example.
SQL
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, vehicleMake VARCHAR(20));
INSERT INTO #tbl (vehicleMake) VALUES
('Chevrolet'),
('Tesla'),
('Audi'),
('Nissan');
DECLARE #tableVariable TABLE (ID INT, vehicleMake VARCHAR(20));
DECLARE #topRows INT = 2;
INSERT INTO #tableVariable
SELECT *
FROM #tbl
ORDER BY id;
SELECT TOP(#topRows) *
FROM #tableVariable
ORDER BY id;
I think you are trying to limit the total number of rows BEFORE the OFFSET/FETCH. If so, you need to put it in a subquery/derived table, and add another ORDER BY:
CREATE FUNCTION [dbo].[GET_CUSTOMER_DATA]
(
-- Add the parameters for the function here
#customerID bigint,
#maxRows int,
#offset int,
#rows int
)
RETURNS TABLE
AS
RETURN
(
SELECT *
FROM (
SELECT TOP (#maxRows) all_needed_columns_here_aliased_if_necessary
FROM SS_CustomerCard AS SS_CC
INNER JOIN SS_PersonalRepresentative AS SS_PR
ON SS_PR.customerID = SS_CC.ID
INNER JOIN SS_ApplicationStatus AS SS_AS
ON SS_AS.CustomerID = SS_CC.ID
WHERE
SS_CC.ID=#customerID
ORDER BY SS_AS.EventDateTime DESC, SS_CC.FirstName DESC, SS_CC.LastName DESC
) t
ORDER BY EventDateTime DESC, FirstName DESC, LastName DESC
OFFSET #offset ROWS
FETCH NEXT #rows ROWS ONLY
)
GO

Update table variable

I have a table variable #searchResult:
DECLARE #searchResult TABLE (
[name_template] NVARCHAR(50),
[record_id] INT,
[record_name] NVARCHAR(50)
);
And table [records]:
CREATE TABLE [records] (
[record_id] INT IDENTITY(1, 1) PRIMARY KEY,
[record_name] NVARCHAR(50)
)
#searchResult contains records with [name_template] filled only. I want to update it with latest [record_id] and [record_name] from [records] table that match [name_template].
I've tried folowing SQL query with no success:
UPDATE #searchResult
SET [record_id] = r.[record_id], [record_name] = r.[record_name]
FROM (
SELECT TOP 1
r.[record_id]
, r.[record_name]
FROM [records] AS r
WHERE r.[record_name] LIKE [name_template]
ORDER BY r.[record_id] DESC
) AS r;
Error message:
Invalid column name 'name_template'.
What is correct syntax to update #searchResult with desired values?
You need to do a CROSS APPLY on the tables.
UPDATE #searchResult
SET [record_id] = r.[record_id],
[record_name] = r.[record_name]
FROM #searchResult SR
CROSS APPLY (
SELECT TOP 1 *
FROM [records]
WHERE [record_name] LIKE [name_template] -- Your wish, but do you really need LIKE matching??
ORDER BY [record_id] DESC
) AS r;
Try this:
UPDATE t
SET [record_id] = r.[record_id],
[record_name] = r.[record_name]
FROM #searchResult t
INNER JOIN
(
SELECT MAX([record_id]) As [record_id]
,[record_name]
FROM [records]
GROUP BY [record_name]
) r
ON r.[record_name] LIKE t.[name_template];
Update:
Seems to be working fine from what I've tested:
Create table and table variable:
CREATE TABLE [records] (
[record_id] INT IDENTITY(1, 1) PRIMARY KEY,
[record_name] NVARCHAR(50)
)
DECLARE #searchResult TABLE (
[name_template] NVARCHAR(50),
[record_id] INT,
[record_name] NVARCHAR(50)
);
Populate with sample data:
INSERT INTO [records] ([record_name]) VALUES('a'), ('a'), ('a'), ('b'), ('b')
INSERT INTO #searchResult ([name_template]) VALUES ('a'), ('b')
Update table variable:
UPDATE t
SET [record_id] = r.[record_id],
[record_name] = r.[record_name]
FROM #searchResult t
INNER JOIN
(
SELECT MAX([record_id]) As [record_id]
,[record_name]
FROM [records]
GROUP BY [record_name]
) r
ON r.[record_name] LIKE t.[name_template];
Check results:
SELECT *
FROM records
SELECT *
FROM #searchResult
DROP TABLE records
Results:
records
record_id record_name
----------- -----------
1 a
2 a
3 a
4 b
5 b
#searchResult
name_template record_id record_name
------------- --------- -----------
a 3 a
b 5 b

How can I pull data from multiple sql tables using a join statement

I'm designing a simple in-office ticket system, and would like to include a field for the party responsible for the next action. To do so right this moment I'm thinking of using tableName and tableID as specifiers for the specific responsible party (could be a technician, customer, or third party, all in different tables)
It would be fine to pull that data in and run another select call using the name of the table as a parameter, but the extra data flow slows things down significantly.
Is there a way to use a single join statement to return the details of the party with a column for the table name and one for the individual table id or is there a better way to store the data from multiple potential tables?
You can use left join to achieve your requirement :-
Set Nocount On;
Declare #OfficeTickets Table
(
Id Int Identity(1,1)
,Column1 Varchar(100)
,PartyType Varchar(1)
,TechnicianId Int Null
,CustomerId Int Null
,ThirdPartyId Int Null
)
Declare #OfficeTickets1 Table
(
Id Int Identity(1,1)
,Column1 Varchar(100)
,TableName Varchar(100)
,TableId Int Null
)
Declare #Technician Table
(
Id Int Identity(1,1)
,TechnicianName Varchar(100)
)
Declare #Customers Table
(
Id Int Identity(1,1)
,CustomerName Varchar(100)
)
Declare #ThirdParty Table
(
Id Int Identity(1,1)
,ThirdPartyName Varchar(100)
)
Insert Into #Technician(TechnicianName) Values
('Technician_1')
,('Technician_2')
,('Technician_3')
Insert Into #Customers(CustomerName) Values
('Customer_1')
,('Customer_2')
,('Customer_3')
Insert Into #ThirdParty(ThirdPartyName) Values
('ThirdParty_1')
,('ThirdParty_2')
,('ThirdParty_3')
,('ThirdParty_4')
Insert Into #OfficeTickets(Column1,PartyType,TechnicianId,CustomerId,ThirdPartyId) Values
('ABC','T',3,Null,Null)
,('XYZ','C',Null,2,Null)
,('PUQ','P',Null,Null,4)
Insert Into #OfficeTickets1(Column1,TableName,TableId) Values
('ABC','Technician',3)
,('XYZ','Customers',2)
,('PUQ','ThirdParty',4)
---- taken separate columns for parties
Select ot.Id
,ot.Column1
,t.TechnicianName
,c.CustomerName
,tp.ThirdPartyName
From #OfficeTickets As ot
Left Join #Technician As t On ot.PartyType = 'T' And ot.TechnicianId = t.Id
Left Join #Customers As c On ot.PartyType = 'C' And ot.CustomerId = c.Id
Left Join #ThirdParty As tp On ot.PartyType = 'P' And ot.ThirdPartyId = tp.Id
---- by TableName and TableId
Select ot.Id
,ot.Column1
,t.TechnicianName
,c.CustomerName
,tp.ThirdPartyName
From #OfficeTickets1 As ot
Left Join #Technician As t On ot.TableName = 'Technician' And ot.TableId = t.Id
Left Join #Customers As c On ot.TableName = 'Customers' And ot.TableId = c.Id
Left Join #ThirdParty As tp On ot.TableName = 'ThirdParty' And ot.TableId = tp.Id
output:-

optimizing SQL query with multiple keys

I have a table in a database with a primary key, and a 'second' key as well. This second key can have the same value occur more than once in the table, but often i only want to return the most recent row for that second key. I have an existing query that works below, but I feel like it's very ugly and there should be a simpler way to do this instead of creating a table variable, going through a loop, and inserting 1 row into the table variable on each pass through the loop. Am i making this too hard?
declare #RowCnt int
declare #MaxRows int
declare #secondID as uniqueidentifier
DECLARE #retList TABLE(
firstGUID uniqueidentifier,
secondGUID uniqueidentifier,
name nvarchar(50),
DateCreated datetime
)
select #RowCnt = 1
declare #Import table (rownum int IDENTITY (1, 1) Primary key NOT NULL , secondGUID uniqueidentifier)
insert into #Import (secondGUID) SELECT DISTINCT dbo.TestTable.secondGUID FROM dbo.TestTable
select #MaxRows=count(*) from #Import
while #RowCnt <= #MaxRows
begin
select #secondID=secondGUID from #Import where rownum = #RowCnt
INSERT INTO #retList
SELECT TOP (1) firstGUID,secondGUID,name,datecreated
FROM dbo.TestTable
WHERE dbo.TestTable.secondGUID = #secondID
ORDER BY DateCreated Desc
Set #RowCnt = #RowCnt + 1
END
select * from #retList
EDIT:
for example, imagine the table has these values
firstGUID secondGUID Name DateCreated
EAD50999-E9B1-43F0-9FA6-615405FA5A9A 6163B6ED-6AF4-494E-ACE6-184F4804847B Test1 2014-04-11 15:12:36.303
A9645486-1021-4E98-92AC-1205CC3FB9D3 6163B6ED-6AF4-494E-ACE6-184F4804847B Test2 2014-04-10 15:21:46.087
DEE375BB-BFAF-44BE-AC64-06D7702E2ACB 3BD0A2F0-4E44-43B9-BD24-003B518609C7 Test3
2014-04-11 15:22:37.097
I only want the Test1 and Test3 rows to be returned.
You could use SQLServer's analytical functions:
select firstGUID, secondGUID, name, datecreated
from (select t.*,
rank() over (partition by secondGUID order by datecreated desc) r
from TestTable t) ilv
where r=1
I'm not 100% sure I understand what you're asking, but it sounds like you want to select only the rows containing the max DateCreated. The normal way of doing that is to join with a subselect that uses a group by clause, eg.:
select tt.*
from TestTable tt
join (
select firstguid, max(DateCreated) as maxdate
from TestTable
group by firstguid
) gtmp on tt.firstguid = gtmp.firstguid and tt.dateCreated = gtmp.maxdate

SQL Server: How to get all child records given a parent id in a self referencing table

Hi I have a table which references itself and I need to be able to select the parent and all it's child records from a given parent Id.
My table is as follows:
ID | ParentID | Name
-----------------------
1 NULL A
2 1 B-1
3 1 B-2
4 2 C-1
5 2 C-2
So for the above example I'd like to be able to pass in a value of 1 and get all the records above.
So far, I've come up with the following recursive table-valued-function but it's not behaving as expected (only returning the first record).
CREATE FUNCTION [dbo].[SelectBranches]
(
#id INT
,#parentId INT
)
RETURNS #branchTable TABLE
(
ID INT
,ParentID INT
,Name INT
)
AS
BEGIN
IF #branchId IS NOT NULL BEGIN
INSERT INTO #branchTable
SELECT
ID
,ParentID
,Name
FROM
tblLinkAdvertiserCity
WHERE
ID = #id
END
INSERT INTO #branchTable
SELECT
br.ID
,br.ParentID
,br.Name
FROM
#branchTable b
CROSS APPLY
dbo.SelectBranches(NULL, b.ParentID) br
RETURN
END
GO
You can try this
DECLARE #Table TABLE(
ID INT,
ParentID INT,
NAME VARCHAR(20)
)
INSERT INTO #Table (ID,ParentID,[NAME]) SELECT 1, NULL, 'A'
INSERT INTO #Table (ID,ParentID,[NAME]) SELECT 2, 1, 'B-1'
INSERT INTO #Table (ID,ParentID,[NAME]) SELECT 3, 1, 'B-2'
INSERT INTO #Table (ID,ParentID,[NAME]) SELECT 4, 2, 'C-1'
INSERT INTO #Table (ID,ParentID,[NAME]) SELECT 5, 2, 'C-2'
DECLARE #ID INT
SELECT #ID = 2
;WITH ret AS(
SELECT *
FROM #Table
WHERE ID = #ID
UNION ALL
SELECT t.*
FROM #Table t INNER JOIN
ret r ON t.ParentID = r.ID
)
SELECT *
FROM ret
Recursion in CTE looks bit expensive, so I have wrote this function which make use of recursive function call but much faster that CTE recursion.
CREATE FUNCTION [dbo].[Fn_GetSubCategories]
(
#p_ParentCategoryId INT
) RETURNS #ResultTable TABLE
(
Id INT
)
AS
BEGIN
--Insert first level subcategories.
INSERT INTO #ResultTable
SELECT Id FROM Category WHERE ParentCategoryId = #p_ParentCategoryId OR Id = #p_ParentCategoryId
DECLARE #Id INT
DECLARE #ParentCategory TABLE(Id INT)
DECLARE cur_categories CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY FOR
SELECT Id FROM Category WHERE ParentCategoryId = #p_ParentCategoryId and Id != #p_ParentCategoryId
OPEN cur_categories
IF ##CURSOR_ROWS > 0
BEGIN
FETCH NEXT FROM cur_categories INTO #Id
WHILE ##FETCH_STATUS = 0
BEGIN
--Insert remaining level sub categories.
IF EXISTS(SELECT 1 FROM Category WHERE ParentCategoryId = #Id AND Id != #Id)
BEGIN
INSERT INTO #ResultTable
SELECT DISTINCT C.Id from Fn_GetSubCategories(#Id) C INNER JOIN #ResultTable R ON C.Id != R.Id
END
FETCH NEXT FROM cur_categories INTO #Id
END
--Delete duplicate records
;WITH CTE AS
(SELECT *,ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Id) AS RN FROM #ResultTable)
DELETE FROM CTE WHERE RN<>1
END
CLOSE cur_categories
DEALLOCATE cur_categories
RETURN
END
Unless you are using Oracle, your table structure is not suitable for the problem described. What you are attempting to do is grab a hierarchy (traversing a tree structure).
There is an article, More Trees & Hierarchies in SQL, that describes one method of solving the hierarchy problem. He basically adds a "lineage" column describing the hierarchy to every row.