You can watch the replay for this Geek Sync webcast in the IDERA Resource Center: http://ow.ly/tt9w50A5g7u
Jeffrey will cover ways to avoid people thinking your code is bad, some common coding fallacies, and presents two case studies on rewriting bad SQL. The first case study contains three iterations of code written by the instructor, the second case study comes from an eCommerce site that had a great idea but horrible execution.
2. About Soaring Eagle
Since 1997, Soaring Eagle Consulting has been helping enterprise clients improve their overall system performance at the database
tier, arguably the most volatile and critical component of distributed application architecture. Our clients range in size from fledgling
startups through Fortune 100 companies and leading financial institutions.
Soaring Eagle has been a leader in software development, architecture, performance and tuning databases, while promoting
mentoring and training all over the world for over a decade. Many of our employees, and partners have written books, speak at
seminars about leading edge technologies. We have expertise in all business tiers, financial; health, manufacturing, government
agencies and many ecommerce businesses.
Consulting
• Performance & Tuning
• Data Performance
Management
• Emergency Triage
• Performance & Security
Audits
• Staff Augmentation
• Project management
• Database architecture
• Scalability assessment
and planning
Training
• Onsite/Web based
• Microsoft
• Sybase
• Oracle
• APM
• Six Sigma
Software
• Application Performance
Management
• Database performance
accelerator
• Database performance
management
• Database Security
Managed
Services
• Remote Database
Management
• Performance
management
• Emergency db Service
• Proactive mitigation
• Problem notification
• Problem resolution
9
3. Microsoft SQL server, SQL EM,
Query Analyzer are all trademarks of Microsoft Inc.
This presentation is copyrighted.
This presentation is not for re-sale
This presentation shall not be used or modified without
express written consent of Soaring Eagle Consulting,
Inc.
Acknowledgements
Page 6 - 3
4. Why other people’s code stinks
• Poor performance
• Poor documentation
• Inadherence to standards
5. • From a real-life problem
• Your task is to migrate from a source
database to a new target, which has
incremental values on the cost sheet detail
information.
• Your task is to make fun of the first two
options, and to understand the third
Case Study #1:
Very Bad, Bad, Not so Bad
6. CREATE TABLE #WKCostSheet(
WKCostSheetHeaderID int NOT NULL,
Sequence int identity,
ItemDescription varchar(50) NULL,
Value money NULL,
GiftInKind money NULL,
KWNAmount money NULL,
CheckNumber varchar(50) NULL)
Very Bad
7. CREATE TABLE #WKCostSheet2(
WKCostSheetHeaderID int NOT NULL,
Sequence int,
ItemDescription varchar(50) NULL,
Value money NULL,
GiftInKind money NULL,
KWNAmount money NULL,
CheckNumber varchar(50) NULL
)
Very Bad (continued)
9. SELECT WKCostSheetHeaderID, exp ,val ,gik ,kwn ,chkNum
FROM
KWN_Access_Admin.dbo.CostSheet c join
KWN_dev.dbo.WKCostSheetHeader h
on h.WKID = c.KID and
h.WKCostSheetTypeID= c.WishNum
where
KID in (select WKID from dbo.WKs)
order by
WKCostSheetHeaderID
Very Bad (cont’d)
10. insert
#WKCostSheet2 select * from #WKCostSheet
create clustered index a on
#WKCostSheet2 (WKCostSheetHeaderID,
Sequence)
Very Bad (cont’d)
11. while exists (select * from #WKCostSheet2)
begin
INSERT INTO KWN_dev.dbo.WKCostSheet
(WKCostSheetHeaderID
,Sequence
,ItemDescription
,Value
,GiftInKind
,KWNAmount
,CheckNumber
)
Very Bad (cont’d)
12. select * from #WKCostSheet2
where
WKCostSheetHeaderID =
(select MIN (WKCostSheetHeaderID)
from #WKCostSheet2)
delete from #WKCostSheet2
where WKCostSheetHeaderID =
(select MIN (WKCostSheetHeaderID)
from #WKCostSheet2)
update #WKCostSheet2
set Sequence = Sequence –
(select MIN ( sequence) from #WKCostSheet2 ) + 1
end -- While
Very Bad (cont’d)
13. • Multiple temp tables
• Enormous amount of IO from second temp
table, heavily due to the large quantity of
deletions & multiple passes through the
temp table
Very Bad – summary
14. CREATE TABLE #WKCostSheet(
WKCostSheetHeaderID int NOT NULL,
Sequence int identity,
ItemDescription varchar(50) NULL,
Value money NULL,
GiftInKind money NULL,
KWNAmount money NULL,
CheckNumber varchar(50) NULL)
Less bad
15. INSERT INTO #WKCostSheet
/* (SAME) */
SELECT WKCostSheetHeaderID
/* (SAME) */
create clustered index a on
#WKCostSheet(WKCostSheetHeaderID,
Sequence)
Less bad (cont’d)
17. Less bad (cont’d)
select a.WKCostSheetHeaderID
,a.Sequence - b.minseq + 1
,a.ItemDescription ,a.Value ,a.GiftInKind
,a.KWNAmount ,a.CheckNumber
from #WKCostSheet a join
(select c.WKCostSheetHeaderID,
MIN (c.sequence) as minseq
from #WKCostSheet c
group by WKCostSheetHeaderID) as b
on a.WKCostSheetHeaderID = b.WKCostSheetHeaderID
18. • Still need a temp table
• Join is far less costly than the repeated
deletions
Less bad – summary
19. SELECT
WKCostSheetHeaderID, row_number() over
(partition by WKCostSheetHeaderID order by val)
,exp ,val ,gik ,kwn ,chkNum
FROM KWN_Access_Admin.dbo.CostSheet c join
KWN_dev.dbo.WKCostSheetHeader h on h.WKID =
c.KID and h.WKCostSheetTypeID= c.WishNum
where KID in (select WKID from dbo.WKs)
order by WKCostSheetHeaderID
Not so bad
20. • No temp table, no join, no trouble
Not so bad – summary
21. • We’re fans of data-driven design; anything that keeps us from having to push code
back through QA is a good thing. But, we’re going to make fun of the code that
accesses the hierarchical data (note: It was written in SQL Server 2005, prior to
hierarchy IDs being available), starting with naming conventions. The below character
string is a table name.
• [_SynComs.Orders.OrderItem.product->SynComs.Products.PrinterCartridge]
• Nontrivial to type, contains special characters… not a lot right with this.
• The interesting thing, from their perspective, is that the same query is used for every
single database call. That’s right, one query only for every access. The catch is,
there’s an unlimited number of recursive calls to get the database results, and the
structure was set up to put real (data) information into the physical schema, a nifty
way to create extra contention in the system tables.
• For the record, the CTE changed approach brought query time from 9.5 seconds
down to .23 seconds.
Case Study #2:
From horrid code to CTE
22. SELECT
0 as generation,
major_id as tableId
into #tblguid
FROM
sys.extended_properties
WHERE (value in
(
'SynComs.Orders.Order, SynComs'
)
and name = 'ParentType')
create clustered index CItablID on #tblguid(tableId)
Original Code
23. declare @generation int
select @generation=0
while (1=1) begin
select @generation=@generation+1
insert into #tblguid (generation, tableId)
SELECT @generation, parent.major_id
FROM
#tblguid tbl JOIN
sys.extended_properties child
on tbl.tableId = child.major_id and
child.name = 'ChildType' and generation = @generation -1
JOIN sys.extended_properties parent on
child.value = parent.value and parent.name = 'ParentType'
Original Code (cont’d)
24. where not exists
(select *
from
#tblguid lookitup
where
parent.major_id = lookitup.tableId)
if (@@ROWCOUNT=0) break
end
select
name as tableName
from
sys.tables join
#tblguid
on object_id = tableId
Original Code (cont’d)
25. /*
Do you like this? We’re about to recursively create
/ execute a large view… good candidate for
rewrite / approach change
*/
Original Code (cont’d)
26. declare @string varchar(max)
select @string = '
create view my_view as
select * from
[_SynComs.Orders.Order.billingAddress-
>SynComs.Customers.CustomerAddress]
union all /* At least it’s “union all” here */
select * from [_SynComs.Orders.Order.discounts-
>SynComs.Orders.Discounts.Discount]
/* … for brevity, I’ve removed about 12 more of these */
exec (@string)
go
Original Code (cont’d)
27. select 0 as generation, parentObjectGuid,
childObjectGuid, fieldName, parentType, childType,
_guid_, _pk_
into
#guids
from
my_view
where
parentObjectGuid IN (
'3ee588d1-2096-4ddb-adc6-
d5a140725721',/* about 70 more removed */);
Original Code (cont’d)
28. update @guids28927 set generation=0
create clustered index CI_GUID on #guids (_guid_)
create nonclustered index NCI_childobject_generation on
#guids (generation,childObjectGuid,parentObjectGuid)
declare @generation int
select @generation=0
Original Code (cont’d)
29. while (1=1)
begin
select @generation=@generation+1
insert into #guids
(generation, parentObjectGuid, childObjectGuid, fieldName, parentType,
childType, _guid_, _pk_)
select @generation, parentObjectGuid, childObjectGuid, fieldName,
parentType, childType, _guid_, _pk_
from my_view
where
parentObjectGuid in (select childObjectGuid from #guids where
generation=(@generation-1))
and
not exists (select * from #guids where
my_view._guid_ =#guids._guid_ )
Original Code (cont’d)
30. if (@@ROWCOUNT=0) break
end
Select parentObjectGuid, childObjectGuid, fieldName,
parentType, childType, _guid_, _pk_
from
#guids
Original Code (cont’d)
31. WITH RecursionRelationship (
generation, parentObjectGuid, childObjectGuid,fieldName, parentType,
childType, [_guid_], [_pk_] )
AS
(
-- Anchor Query
select 0 as generation, parentObjectGuid, childObjectGuid,
fieldName,parentType, childType, _guid_, _pk_
from
dbo.ObjectRelationship
where
parentObjectGuid IN (
'3ee588d1-2096-4ddb-adc6-d5a140725721', /* same list as above */)
Revised Code
32. UNION ALL
-- Recursion Query
Select r_r.generation +1, o_r.parentObjectGuid, o_r.childObjectGuid,
o_r.fieldName, o_r.parentType, o_r.childType, o_r._guid_, o_r._pk_
from
dbo.ObjectRelationship o_r JOIN RecursionRelationship r_r on
o_r.parentObjectGuid = r_r.childObjectGuid)
select parentObjectGuid, childObjectGuid, fieldName, parentType,
childType, _guid_, _pk_
from
RecursionRelationship option (maxrecursion 32767)
Revised Code (cont’d)
34. Soaring Eagle Flight Center FREE! For 3 months
• Why Try Soaring Eagle Flight Center?
• See your environment health in seconds
• The hundreds or thousand email alerts are corralled
into alerts that you control
• Flight provides Predictive Analysis Tools
Email sales@soaringeagle.guru to take
advantage