Blog ala Vidar

SQL, AppFrame and other cool technologies

Tag Archives: SQL Server

Indenting

Have any of you ever had problems with understanding a query because of the (lack of) indenting? I’ll bet the answer is “Yes, MANY times!”.

Today, as you’ve probably already understood, I’m going to take a look at how easy indenting is, and how much time it saves you, me, Bill Gates and all other poor creatures. Some may call me strict when it comes to indenting, but I think it’s a good thing. I’m not saying everyone should follow my standard. I just want to spread the word and make people understand.

We can start with an example from an old view in AppFrameR2 (aviw):

Before:

select d.title as "teamdoctitle", e.*,
(select count(*) from stbv_calendar_events e2 where e2.copiedfromeventref=e.copiedfromeventref and e.copiedfromeventref is not null) copycount,
cast(case when exists(select * from stbl_calendar_attendance where login = suser_sname() and eventref = e.primkey) then 1 else 0 end as bit) as userattend
from stbv_calendar_events e (nolock) left outer join stbv_teamdoc_documents d (nolock) on d.primkey=e.teamdocref where e.primkey = @primkey

After:

SELECT D.Title AS TeamDocTitle, E.*,
  (SELECT COUNT(*)
        FROM stbv_Calendar_Events AS E2
        WHERE E2.CopiedFromEventRef=E.CopiedFromEventRef
          AND E.CopiedFromEventRef IS NOT NULL) CopyCount,
  CAST(CASE WHEN EXISTS(SELECT *
                            FROM stbl_Calendar_Attendance
                            WHERE Login = SUSER_SNAME()
                              AND EventRef = E.PrimKey)
                    THEN 1
               ELSE 0
               END AS BIT) AS UserAttend
    FROM stbv_Calendar_Events AS E WITH (NOLOCK)
    LEFT OUTER JOIN stbv_TeamDoc_Documents AS D WITH (NOLOCK) ON D.PrimKey = E.TeamDocRef
    WHERE E.primkey = @PrimKey

As you see, it’s difficult to see what’s SQL and what’s not. It is also very hard to know where the subqueries end.

Imagine you have a MUCH longer query. Sviw_MSAccessCR2_MyMenuObjects for instance. In this view you have alot of case, subqueries (with joins) and both inner and outer joins. Try reading that without indenting, syntax highlight and uppercasing SQL-commands! That’s not even possible! About a year ago we had some performance issues with this view. I remember my thoughts:
– ”EHM! Where do I start?!”

It was about 13 kilometers longer than the one today, it had alot of unions and other ”fancy” stuff. I think I used almost a day only ”getting to know” (indenting/casing) the view! If possible, take a look at it, remove all indenting and change all text to lowercase (mark it and press ctrl + shift + L). Copy it to notepad and try to figure out where the subqueries end, where each case ends etc. It’s almost impossible.

Very often I get questions like ”Could you help me with a SQL-query?” on mail and/or MSN. To understand the logic, I always start with indenting (if it’s not already indented). I’ll bet $100 that most of these questions wouldn’t be asked if the person already had indented the query, because it’s so much easier to find the issue with, than without.

It takes me more time to write a query WITHOUT indenting and casing than WITH, because I’m so used to this method

.. and it’s MUCH more difficult to read. My advice: Use indenting and uppercase on SQL-commands. If it’s ”my” standard or some improved (if that’s possible 😉 standard does not matter. ANYTHING is easier to read than one-line-plain-sql-queries!

24th

Chit-chat:

We just had “Pizza & Learning” here in Ølen. My opinion: very successful 🙂 First, Peter startet with showing all available functionality in the grid-control. After half an hour, came FINALLY the pizza-guy. 14(?) HUNGRY computer-geeks to feed!

After some pizza and chit-chat, I started trasking about the new features in SQL 2005, compared to 2000. This includes a couple of new functions and fields (ROW_NUMBER, RANK, VARCHAR(MAX) etc).

But, wait. THERE’S MORE!

Something that can be, in my opinion, VERY useful for developers is the new Dynamic Management Views and Functions (DMV / DMF). This is internal views, which gives you ALOT of (un)usefull information about queries, indexes etc. My example was the following:

SELECT * FROM sys.dm_db_index_physical_stats (5, 665678065, NULL , NULL, 'LIMITED')

5 = DB_ID(‘AppFrameR2’)
665678065 = OBJECT_ID(‘stbl_System_Users’)

This returns everything worth knowing about the indexes on stbl_System_Users. What you should notice here is the “avg_fragmentation_in_percent” column. If this is high, you might want to rebuild your index. This is why I have made a new SP: sstp_System_RebuildIndexes

First time I ran that on AppFrameR2 it took somewhere between 3 and 5 hours (goldfish memory). Now it only takes 30-45 minutes, and I’ve run it each weekend since the first time i ran it. OBS!! I do NOT recommend running this within workhours! This will slow down, and sometimes LOCK several tables! Therefore: Run it at night, when everyone SHOULD be in their bed.

Torgeir’s answer to everything:
One other thing, that’s also available in 2000: PROFILER!

Each time I ask Torgeir (Omegas tech. guru number UNO?) a question related to databases, he ALWAYS (I’m not kidding!) answers: Have you tried the profiler?
… and each time I get that answer, I get red like a tomato and mumble his answer to myself. 90% of the time, this helps, the other times, I ask again, and get a “real” answer 😛

Profiler can help you with alot of things. SQL “debuging” of queries, what happens in stored procedures, checking WHAT’S taking so long and alot of other cool things. If you haven’t tried it: WHY NOT? PS! If anyone’s wondering if I got a “What would Torgeir do” t-shirt: No, actually not. The norwegian mail-service has lost it!

Arild’s cute little query:

SELECT TOP 5 total_worker_time/execution_count 'Avg CPU Time',
  SUBSTRING(st.text, (qs.statement_start_offset/2)+1,
  ((CASE qs.statement_end_offset WHEN -1 THEN DATALENGTH(st.text)
    ELSE qs.statement_end_offset END - qs.statement_start_offset)/2) + 1)statement_text
        FROM sys.dm_exec_query_stats AS qs
        CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st
        ORDER BY total_worker_time/execution_count DESC

You might want to try this cute little query, just for fun. Or you might end up with tuning the result. Who knows 😉
Anyways, open your copy of books online, and in the “Look for:” textbox, write: sys.dm_db
You’ll get a list of dynamic management views and functions. This is excellent reading material on a saturday night, with a glass, or maybe a bottle?, of wine 🙂