|
|
|
|
-
One of the dynamic management views (DMVs) that is very useful in troubleshooting query performance is sys.dm_exec_requests. The documentation around this DMV, however, is quite lacking in two areas.
percent_complete
This column shows the "percent of work completed for certain operations, including rollbacks." Okay, great, now could you tell us WHICH certain operations? Through experimenting with commands that I thought could be slow and/or interesting, I came up with this list of commands that *do* report percent_complete:
- backup / restore
- dbcc checkdb / checktable / etc.
- dbcc shrinkdatabase / shrinkfile
- dbcc indexdefrag
- alter index reorganize
- rollback operations
And this list of commands which I thought might report percent_complete, but do not:
- create / drop database
- create / drop index / statistics
- alter index rebuild
- waitfor delay / time
- drop table
- truncate table
- any DML operations whatsoever
Do you know of anything that should be in either list?
samplesIn the SQL Server 2005 topic, there are three samples which are just not very good IMHO. Worse yet, in the SQL Server 2008 topic, the samples have been removed completely. The first one explains how to find the query text for a running batch, and it basically says run the following:
| SELECT * FROM sys.dm_exec_requests; |
Then pick the spid you are interested in, copy the value from the sql_handle column, and paste it into this query:
| SELECT * FROM sys.dm_exec_sql_text(< copied sql_handle >); |
Yuck! How about doing this in one step:
SELECT [spid] = r.session_id, [database] = DB_NAME(r.database_id), r.start_time, r.[status], r.command, /* add other interesting columns here */ [obj] = QUOTENAME(OBJECT_SCHEMA_NAME(t.objectid, t.[dbid])) + '.' + QUOTENAME(OBJECT_NAME(t.objectid, t.[dbid])), t.[text] FROM sys.dm_exec_requests AS r CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS t WHERE r.session_id <> @@SPID AND r.session_id > 50 /* -- optionally: AND r.session_id IN (< list of interesting spids >) */ ; |
The second sample does something similar with transactions. Run a select * from dm_exec_requests, take the transaction_id, and copy it into a query against sys.dm_tran_locks. How about:
SELECT [spid] = r.session_id, [database] = DB_NAME(r.database_id), r.start_time, r.[status], r.command, [obj] = QUOTENAME(OBJECT_NAME(t.resource_associated_entity_id, r.database_id)), /* add other interesting columns here */ t.request_mode, t.request_type, t.request_status FROM sys.dm_exec_requests AS r LEFT OUTER JOIN sys.dm_tran_locks AS t ON r.transaction_id = t.request_owner_id WHERE t.request_owner_type = N'TRANSACTION' AND r.session_id <> @@SPID AND r.session_id > 50 /* -- optionally: AND r.session_id IN (< list of interesting spids >) */ ; | And finally, the third sample in the 2005 docs shows you how to get all of the sessions that are blocked. How about retrieving both the blocked *and* blocking processes? While this is a much more convoluted example and it involves many more objects, it certainly does a better job of showing off the power of the DMVs:
WITH blocking_info AS ( SELECT [blocker] = wait.blocking_session_id, [waiter] = lock.request_session_id, b_handle = br.[sql_handle], w_handle = wr.[sql_handle], [dbid] = lock.resource_database_id, duration = wait.wait_duration_ms / 1000, lock_type = lock.resource_type, lock_mode = block.request_mode FROM sys.dm_tran_locks AS lock INNER JOIN sys.dm_os_waiting_tasks AS wait ON lock.lock_owner_address = wait.resource_address INNER JOIN sys.dm_exec_requests AS br ON wait.blocking_session_id = br.session_id INNER JOIN sys.dm_exec_requests AS wr ON lock.request_session_id = wr.session_id INNER JOIN sys.dm_tran_locks AS block ON block.request_session_id = br.session_id WHERE block.request_owner_type = 'TRANSACTION' ) SELECT [database] = DB_NAME(bi.[dbid]), bi.blocker, blocker_command = bt.[text], bi.waiter, waiter_command = wt.[text], [duration MM:SS] = RTRIM(bi.duration / 60) + ':' + RIGHT('0' + RTRIM(bi.duration % 60), 2), bi.lock_type, bi.lock_mode FROM blocking_info AS bi CROSS APPLY sys.dm_exec_sql_text(bi.b_handle) AS bt CROSS APPLY sys.dm_exec_sql_text(bi.w_handle) AS wt; | So, now what?
Well, I complained about these issues to some extent in the following Connect items:
https://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=354545
https://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=284207
The former is fairly new, and I am awaiting some response. The latter was closed as "fixed" but this made no sense -- the topic has actually taken a step backward since I lodged my initial complaint. I re-opened it earlier today.
|
-
Buck makes a quick mention of the "green" efforts at Microsoft in his blog, and points us to the Environment site that Microsoft has created to show off what they are doing for a cause that is important to all of us. There are several tidbits in here that can help you do your part, as well. Please pass the word along.
|
-
As Andrew Fryer pointed out earlier today on his blog, RC0 for SQL Server 2008 has been released to MSDN and TechNet subscribers. What is RC0? It may be the last public push we see before the product RTMs later this year. It is feature complete, and from this point forward, only major show stoppers will likely be addressed before then.
I am writing to plead for your votes on a very important issue that came up for me using the new Activity Monitor, which is a show stopper for me. There is a great new Activity Monitor which has a lot of new features, such as launching Profiler directly from a SPID list, and launching a showplan from a list of "Recent Expensive Queries." This latter feature is completely broken for me, when connecting to a SQL Server 2005 instance. I get an unhandled exception: 
I am hoping that you find this an important issue as I do... most notably because we will likely be upgrading our workstation client tools long before we upgrade all of the servers we manage. So, I am asking for your votes in the following Connect item: http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=349494
|
-
SQL Server's new logo has been published:

Courtesy of Wesley. And I have taken a screen shot of the new splash screen for Management Studio:
|
-
-
As you may know, I have been heavily involved in testing a lot of the new features in SQL Server 2008, and am likely as excited as anybody about its release. Since I work at a fairly progressive company, I spoke with two of my superiors today - independently - and I was surprised at the results. My proposal was, for the project that we are currently working on, that we hit the ground running by deploying SQL Server 2008 when we are ready to launch. Based, of course, on successful testing and adequate performance of RC0 (when we get it) in the meantime, and that the product ships on time. My immediate superior was all for it. Having attended one of my presentations on the new features, he knew about some of the benefits we would enjoy pretty much right out of the box -- page/row/backup compression, filtered indexes, date columns, change data capture, the list goes on. And he is all for deploying the next CTP to our QA environment for serious functionality / load testing and analysis, with the intention of being on the "early adopter" side of the curve when the product ships.
His boss, however, is a lot more cautious. Not only is he uninterested in deploying SQL Server 2008 right away; he is not even interested in looking at it until SP1 is out, and tested, and has about a month of serious market penetration. Which, by rough calculations, based on the TPC benchmark publication date and the new servicing model, should be sometime in February or March of next year. All of this stemming from the long-standing tradition of never installing a dot-oh release of a Microsoft product. Personally, I found the RTM of SQL Server 2005 a hell of a lot more stable than SP2. (And SP1, IIRC, did little in terms of "fixing" anything except that they finished the database mirroring functionality.) Service packs in SQL Server 2000 don't exactly give us great confidence, either. YMMV.
What I was hoping for was a balanced response, somewhere in the middle. Like, okay, we won't deploy the day it is out, but we will perform all the necessary tests, including upgrade scenarios, and consider it within a few months of release. For me, 9 months is a LONG time to wait (and no, it has nothing to do with having children :-)). But are you facing similar superstition? Do you feel that way yourself? I am curious how others are progressing in the "let's upgrade" battle. Tell me your stories! Do you align with my boss' boss? Or do you have any suggestions for changing his mind?
|
-
Currently, the plans for IntelliSense are to support SQL Server 2008 *ONLY*... since it works against SQL Server 2005 in the February CTP, I was very surprised to learn that it is being dropped by RTM (and possibly by RC0). This is mainly because of the time frame of the release and the difficulty of catering to multiple dialects (sometimes there are going to be false positives, and there are even possibilities that it will miss obvious issues). You can read more about this issue at The W Blog.
Being that IntelliSense isn't even going to be complete when SQL Server 2008 ships (mostly just supporting SELECT operations), I could live with a disclaimer that says, when you are working against a downlevel version, it is not always going to be 100% accurate. Could you? If so, I urge you to cast your vote on Connect (at publish time, only 11 people have voted):
http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=341872
|
-
A frequently asked question that surfaced again today is, "how do I see when my data has been accessed last?" SQL Server does not track this information for you. SELECT triggers still do not exist. Third party tools are expensive and can incur unexpected overhead. And people continue to be reluctant or unable to constrain table access via stored procedures, which could otherwise perform simple logging. Even in cases where all table access is via stored procedures, it can be quite cumbersome to modify all the stored procedures to perform logging. SQL Server 2008 will offer Server Auditing for all actions, and this can be logged to a file, or to the Windows Application or Security Log. You can do something as narrow as record when a specific login queries AdventureWorks.Person.Address.City, and as wide as recording information about every query against every database on the entire instance. Here is a quick sample that audits all select queries against Person.Address in the AdventureWorks sample database:
USE master; GO CREATE SERVER AUDIT Test_Server_Audit TO FILE ( FILEPATH = 'C:\Audits\' ); GO ALTER SERVER AUDIT Test_Server_Audit WITH (STATE = ON); GO
USE AdventureWorks; GO CREATE DATABASE AUDIT SPECIFICATION Test_Database_Audit FOR SERVER AUDIT Test_Server_Audit ADD (SELECT ON Person.Address BY PUBLIC) WITH (STATE = ON); GO
SELECT * FROM Person.Address; GO
SELECT * FROM fn_get_audit_file('C:\Audits\*', NULL, NULL); GO
USE AdventureWorks; GO ALTER DATABASE AUDIT SPECIFICATION Test_Database_Audit WITH (STATE = OFF); GO DROP DATABASE AUDIT SPECIFICATION Test_Database_Audit; GO USE master; GO ALTER SERVER AUDIT Test_Server_Audit WITH (STATE = OFF); GO DROP SERVER AUDIT Test_Server_Audit; GO |
For those of us who don't want to wait for SQL Server 2008 and cannot use stored procedures to log select activity, there is another answer: the DMV sys.dm_db_index_usage_stats, introduced in SQL Server 2005. By showing the last read and write to a table, this DMV allows us to answer the questions we couldn't before:
- when was database x accessed last?
- when was table y accessed last?
We can answer the question about access to a database simply by aggregating the data in the DMV to the database level:
USE AdventureWorks; GO
SET ANSI_WARNINGS OFF; SET NOCOUNT ON; GO
WITH agg AS ( SELECT last_user_seek, last_user_scan, last_user_lookup, last_user_update FROM sys.dm_db_index_usage_stats WHERE database_id = DB_ID() ) SELECT last_read = MAX(last_read), last_write = MAX(last_write) FROM ( SELECT last_user_seek, NULL FROM agg UNION ALL SELECT last_user_scan, NULL FROM agg UNION ALL SELECT last_user_lookup, NULL FROM agg UNION ALL SELECT NULL, last_user_update FROM agg ) AS x (last_read, last_write); |
Switching focus to each table is accomplished by adding the object name to the GROUP BY (and as Jerry pointed out, this will require SP2 to use OBJECT_SCHEMA_NAME(), otherwise you can join against sys.tables and sys.schemas):
|
USE AdventureWorks; GO
SET ANSI_WARNINGS OFF; SET NOCOUNT ON; GO
WITH agg AS ( SELECT [object_id], last_user_seek, last_user_scan, last_user_lookup, last_user_update FROM sys.dm_db_index_usage_stats WHERE database_id = DB_ID() ) SELECT [Schema] = OBJECT_SCHEMA_NAME([object_id]), [Table_Or_View] = OBJECT_NAME([object_id]), last_read = MAX(last_read), last_write = MAX(last_write) FROM ( SELECT [object_id], last_user_seek, NULL FROM agg UNION ALL SELECT [object_id], last_user_scan, NULL FROM agg UNION ALL SELECT [object_id], last_user_lookup, NULL FROM agg UNION ALL SELECT [object_id], NULL, last_user_update FROM agg ) AS x ([object_id], last_read, last_write) GROUP BY OBJECT_SCHEMA_NAME([object_id]), OBJECT_NAME([object_id]) ORDER BY 1,2; |
One word of note is that sometimes an UPDATE can look like a simultaneous read and write. For example:
USE AdventureWorks; GO UPDATE Person.Address SET City = City + ''; GO SELECT * FROM sys.dm_db_index_usage_stats WHERE database_id = DB_ID() AND index_id = 1 AND [object_id] = OBJECT_ID('Person.Address'); GO |
See that for index_id 1, last_user_scan and last_user_update are identical and fairly recent. Another note is that unless a view is indexed, you cannot reliably track access to a view -- instead the references to the underlying tables are updated in the DMV. UPDATE - Mike C# and dave ballantyne brought up a great point that applies to all DMVs: the values do not survive a SQL Server restart, or detach/attach, or even Auto-Close. So, if you restart your server and then want to see when something was last accessed, all objects will either be NULL or very recent. One way to work around this is to create a SQL Server Agent job that polls the DMV periodically, and stores a snapshot of the data. This way you can have a running history of "last access" and maybe roll it up once per day (or whatever granularity is suitable). Even when SQL Server 2008 is released, auditing of some kind will be required if you want more information, such as a history of who ran which queries. And if you are looking for more details about information that has been added, updated or deleted, you are going to want to look into the Change Tracking and/or Change Data Capture features. But in the meantime, this DMV provides a quicker and much lighter-weight approach to at least determining when your data was accessed last.
|
-
As you may already know, I am not a big fan of the MONEY data type, because of its inflexibility, accuracy problems, and the expectations the name of the type evokes in new users. If I had my way, MONEY would become a synonym for DECIMAL in SQL Server 2008 (allowing for specific precision and scale), and be removed in the following version. Of course there are people out there that either don't feel as strongly as I do, or feel the opposite -- that MONEY should be here to stay.
After a recent discussion about the pros and cons of using MONEY vs
DECIMAL for storing currency (and even non-currency) data, curiosity
got the better of me. One of the arguments for the MONEY data type was performance. No supporting data was provided, of course. So I decided to conduct some tests myself. I wanted to measure how MONEY compared to DECIMAL data types both in their original implementations and using new technologies available in SQL Server 2005 (VARDECIMAL storage format) and SQL Server 2008 (page and row compression).
The person arguing for MONEY showed the space used by MONEY compared to the same information stored in a DECIMAL(20,4) column. Not all that surprisingly, the latter was slightly larger. But is that the whole story? No, for two reasons. One is that the *performance* of these choices was not compared, and the other is that DECIMAL(20,4) is not a very realistic requirement for storing currency data. Unless you are storing the pricing information for luxury yachts or aircraft carriers, in which case you can probably drop the decimal places altogether and use INT or BIGINT. For the rest of us, a better choice would be DECIMAL(8,2) or DECIMAL(10,2).
I created 11 databases, each with a single table containing a single column:
Keeping these tables in separate databases allowed for isolation of several factors and measurements, including database level settings, log growth, data file size and even backup time.
Next, I populated the table in each database with approximately 390,000 rows of varying length decimal data (based on calculations against object_id from a triple cross join of sys.objects on itself), and measured the insert times and storage requirements. Here is how they stacked up:

Then I performed an update that affected all rows, making sure that roughly 20% of the rows would have a significant change in significant digits (e.g. by adding 1,000,000). Here is the performance comparison, as well as how the data and log were affected:
Next I compared the time and cost of performing a SELECT COUNT(*) with a WHERE clause against the column:
And finally, I performed native and compressed backups of each database, comparing execution time and output size:
The following chart summarizes everything performance-wise. The orange with the dot means that database performed the best; the x on the red background means it performed the worst.
And this chart summarizes all things size-wise:
Of course, there is nothing overly definitive here. DECIMAL(10,2) with row compression enabled got the most "first place" metrics, while MONEY with no compression and VARDECIMAL types never finished near the top of the class. But you can judge from the results for yourself, and make decisions based on your own priorities. [UPDATE] Alex asked for some metrics on more complex operations like SUM(). I ran some tests using both SUM() and AVG(). The logical reads of course are the same as all the others, and the scan costs remained unchanged as well. But as for the observed performance of both calculations (compute scalar cost was identical for both operations), see the following chart:

Again, this was an average over 10 tests. Note that I did not append these results to the summary charts I delivered above. And sorry about the slightly different-looking screen shot. [/UPDATE]
Please take into account that these tests were performed on a dual-core laptop computer, and the database files were created on external storage. There are many other tests I could have run to glean more performance and storage data, against a much larger data set, and using production-class hardware, but for the scope of this post I just wanted to glance at the most basic operations. I repeated these tests 10 times from start to finish, so each metric taken is an average of 10 tests (in a lot of cases they were the same every time).
This was a very tedious exercise to perform. If you would like to perform your own tests, with your own sample data, and on your own hardware, I will more than gladly share my scripts. I'd post them here right now, but they are scattered and not distribution-friendly at this point.
|
-
In a recent blog post, Dancho Danchev mis-labeled a recent IIS vulnerability as a "massive SQL injection attack."
Let's be honest here. Yes, this alert needs attention. But this is not a new SQL injection vulnerability. It is simply an exploit in IIS that lets malicious users access your source code. If your database is already open to SQL injection attacks by anyone who can access the file system on your web servers, then yes, SQL injection is just waiting for the next vulnerability to your file system. However, if you protect your database server(s) from SQL injection in the first place, then no IIS vulnerability will magically become known as a SQL injection attack.
Never mind that half the IIS servers in the world probably don't even connect to SQL Server, and of the remainder, not all are vulnerable to SQL injection. The ones that are vulnerable are that way because the web developers and/or DBAs have been sloppy and allowed for practices that help make SQL injection possible.
Call it what it is; don't sensationalize it. And instead of trying to create panic, provide a little education! How do you prevent an IIS vulnerability from becoming a SQL injection attack? There are plenty of things you can do. Some of them are pretty obvious, or have been discussed previously, but I'll recap the ones on my list:
- Do not expose your SQL Server to the Internet directly
While in some cases you can't avoid this (shared database servers at a hosting provider, for example), if your server-side code yields a public address, or enough information that the public address can be easily determined, then you are opening yourself up. All someone needs is read access to your config file or ASP page in order to obtain credentials to access your SQL Server from anywhere. Talk to your network administrator about keeping SQL Server behind your firewall.
- Make your passwords strong
Ideally, your applications will use Windows authentication, but if you must use mixed authentication modes, then make sure your SQL Authentication passwords are "strong" passwords. It is very hard to be completely immune to a dictionary attack, but you can make it much more difficult by using a 16-character password with mixed case and alphanumerics, like '$QL$erver_r0ck$!', as opposed to an "easier" password like 'tweetybird.'
- Follow the principle of least privilege
Do not use sa as the login in the connection strings for your application. Use a low-privileged user that can only execute (certain) stored procedures. There is no reason someone should be able to add a query like "SELECT * FROM sys.objects" to your server-side code, or launch extended procedures like xp_cmdshell, or drop objects, because that user should not have sufficient access to do so... the application user should not be sa or db_owner. Lock down your applications, and only give them the rights they need.
Similarly, do not use a domain administrator or otherwise privileged user as the service account. This would mean that anything that runs under the context of SQL Server has free reign over your server or even entire network, using a variety of tools like extended procedures.
- Always use stored procedures, or at least parameterized statements
If you build ad hoc SQL in your applications, then you are asking for SQL injection attacks, and I strongly suggest you become familiar with using stored procedures or parameterized queries. Otherwise, all input becomes suspect, since it is very easy to use comments or semi-colons to change the meaning of queries or to append additional queries to be executed. With a query that uses strongly typed parameters, however, this technique becomes fruitless. This does not mean something like:
sql = "EXEC dbo.foo @param1 = '" & Request.QueryString("bar") & "'" conn.execute(sql) |
This is still vulnerable to SQL injection, because I can now call the page using ?bar=';drop table blat;--
Instead you should use a command object and pass the inputs to parameters. (This also prevents you from having to escape apostrophes in names like O'Hagan, delimit date literals correctly, etc.)
- Use TRY/CATCH to return more generic error messages
In order to prevent revealing your database structure, do not let errors like foreign key violations or other errors bubble up to the application. This just gives your potential attacker more information about your database structure than they need to have. Instead use error handling to say "That user does not exist" instead of the default error message SQL Server provides -- which gives specific table and column information back to the user. If you are using ASP.Net, then you can make sure that you turn CustomErrorsMode to "On" or "RemoteOnly" and set compilation debug to "false"...
- Do not store passwords in your Users table
A lot of web applications store usernames and passwords so that their users can log in to the application. Instead of storing a password in plain text, which can then be read easily by anyone who manages to gain read access to the Users table, store a hash of the password (using MD5 or something similar). When the user attempts to login (hopefully via SSL), you use the same technique to hash their entry and compare the hashed values, instead of a clear text comparison. Even if the user has read access to the stored procedure that implements the hash, all they can do with it is try and try and try... they cannot reverse engineer the data if you use a proper hashing technique.
|
-
Bob Ward posted a blog entry today where he explains the process of getting a cumulative update for SQL Server 2005 without having to call Microsoft's support team (they are called CSS now but you may remember them as PSS). This is a great evolution in the process, which used to be very difficult (you had to open a support case and be deemed eligible to be issued a CU by a support engineer), and a few months ago they made it a little easier, allowing you to submit a form and have an engineer review it (without a phone call or a formal case), and up to a day later, you got an e-mail providing the download link(s). Now, at least for post-SP2 cumulative updates, the e-mail containing the download is almost immediate. Makes me wonder why they don't just make it downloadable like a service pack, but in any case, this is a great step forward. You can see his post here: http://blogs.msdn.com/sqlreleaseservices/archive/2008/04/15/cumulative-update-7-for-sql-server-2005-service-pack-2-2.aspx
|
-
Just wanted to post a brief warning about expecting to be able to roll back a cumulative update or hotfix by reverting to a restore point. In short: don't do it! These two features are *NOT* designed to work together. There are various complications with this and other methods of removing a cumulative update. Hopefully you won't be in a situation where you need to remove a cumulative update, but if you do, I will go over a few things you should be aware of. Binaries vs. Uninstall
System restore is not necessarily going to remove binaries from locations on your disk that Windows isn't deemed to "own." So what you may end up with in some scenarios is a system that has a cumulative update (partially) installed, but since the program is no longer registered in add/remove programs, there is no longer the ability to remove it correctly. Eventually this may lead to a wipe and reinstall. I strongly recommend using the CU uninstaller utility in Add/Remove Programs instead of trying to "roll back" like you might do with shareware and less complex, non-service-type applications.
Removing "old" setup files You may be very anal about cleaning up old files from your system that are no longer needed. Lets say you install SP2 on SQL Server 2005, then you install CU5. If you install CU6, you may think it is safe to blow away the CU5 files. Hold on, tiger! If you need to uninstall CU6, it is going to hang at some point, and leave you in a bit of a pickle. What happens is it rolls back to SP2 (or whatever release/SP level you were at before applying any CUs) and then tries to apply CU5. If it can't find the install files (because you deleted them!) then you will be stuck at SP2 and I am not sure currently if the uninstall will fail and rollback, or if it will succeed and leave you at SP2 instead of CU5. I have heard that you might get the big hourglass in this scenario, and if so, you may eventually kill it -- potentially leaving your system in a bad state. So, my recommendation here is, leave those installer files there. This should be a very last resort for reclaiming disk space, and with a little foresight, you should run the installers from a system other than C:.
Cluster Scenario Personally, I would highly recommend testing CU functionality on a throw-away cluster (e.g. virtualized) and doing your best to not have to remove a CU once it has been baked into an important cluster. I have successfully removed a CU from one cluster in the past, but I am sure it is not a very highly tested scenario. Both Geoff Hiten and I have had cases where attempting to install an SP or CU onto a cluster took on one node but not the other, and this made it impossible to back out the installation *or* to supercede it with the next SP or CU. Geoff found a work-around after a very lengthy process; I gave up on MS Support after several months, and resigned to rebuilding the cluster from the ground up. Summary In a session in Seattle today, we were told that there would be a KB article forthcoming on how to deal with rolling back SP and CU installs. When I hear about it, I will post something.
|
-
Microsoft has committed publicly to releasing Service Pack 3 for SQL Server 2005. Francois Ajenstat first talked about it very recently here. While there were some casual mentions of the release in some responses to Connect items, I wanted to be sure it was truly public knowledge before I gave it higher visibility. You should expect it in Q4 of this year, after SQL Server 2008 is released.
|
-
While playing with the new Policy-Based Management (PBM) features of SQL Server 2008 the other day, I came across a really annoying syntax implementation that is going to trip up a lot of people unless it is fixed. We all know how DATEADD works:
| SELECT DATEADD(DAY, 1, GETDATE()); |
Sadly, because an expression for a condition is validated using C# and not T-SQL, the syntax needs to be slightly different:
| SELECT DATEADD('DAY', 1, GETDATE()); |
Note the single quotes around 'DAY'... this syntax, obviously, will not work in T-SQL anytime soon:
Msg 1023, Level 15, State 1, Line 1 Invalid parameter 1 specified for dateadd. |
So, imagine I am creating and testing a big WHERE clause in a query window, and once I have it right, I want to copy and paste it into the expression editor for a condition, because I want to use that WHERE clause to enforce or monitor some policy. Now I need to save two versions of it; the original T-SQL version (should I need to modify it later), and the condition-compatible version -- after meticulously adding single quotes by hand (or writing my own parser that will add them for me). For example, would you really want to be going through large expressions like this, and "correcting" them?
| DATEADD('DAY',1-DATEPART(DW,GETDATE()),DATEDIFF('DAY',0,DATEADD('MINUTE',DATEDIFF('DAY',0,GETDATE()),GETDATE()))) |
And this, only if I know in advance that the DATEADD syntax needs to be different (it is not the only function affected, by the way -- DATEPART() and DATEDIFF() have the same restrictions). Note that because I forgot to put single quotes around 'DW', I will get an error message. The error message that I receive is far less than helpful, and is the same regardless of where or how often I forgot single quotes, or even if I left out one of the parentheses:
| Error parsing 'DATEADD('DAY',1-DATEPART(DW,GETDATE()),DATEDIFF('DAY',0,DATEADD('MINUTE',DATEDIFF('DAY',0,GETDATE()),GETDATE())))'. Make sure string constants are enclosed in single quotes and facet properties are prefixed with '@' sign. |
It would be great to have a helpful addendum, indicating at least the first syntax error encountered, for example "Incorrect syntax near 'DW'"...
I espoused about much of this on Connect, of course:
PBM : Expressions like DATEADD require inconsistent and unintuitive syntax
If you think consistency is important, please consider voting. Unfortunately, due to time constraints around the looming RTM date, it is unlikely this will be fixed. But one can hope.
There are some other cases in the past where some decision was made on a developer's computer somewhere, and by the time the decision came into question, it was too late to correct. Well, I have three examples that come to mind immediately, and one of them was, in fact, changed at the last minute.
DATETIME2 This data type covers pretty much everything, doesn't it? Why not be consistent with the INT or CHAR types (BIGDATETIME or DATETIME(MAX))? Do you envision the need for, say, EVENBIGGERDATETIME? Yet the name implies that they are leaving room for a higher-scale or higher-precision date/time data type (DATETIME3?). Or maybe the name was chosen by an employee that was recruited from Oracle.
TIMESTAMP This was a very unfortunate naming blunder, since the data type is equivalent to ROWVERSION and has nothing to do with date or time at all. But the implication given by name alone leads a lot of people to add a TIMESTAMP column to their table, only later to seek help in the forums, asking how to display their TIMESTAMP values as DATETIME, or perform a WHERE clause to filter by DATETIME. We have asked repeatedly that this alias for ROWVERSION be marked as deprecated, and yet in sys.types in SQL Server 2008, TIMESTAMP appears, but ROWVERSION does not. :-(
DATE and TIME as CLR types When SQL Server 2005 was in beta, the SQL team thought it would be great to use DATE and TIME as a way to demonstrate the power of the new CLR types. Unfortunately, the new types did not play well with the other DATETIME data types, the new SSMS GUI, or built-in functions like DATEADD and DATEDIFF. Thankfully, a rather large group of us were so vocal in our complaints, that before ship date, they agreed to cut the feature until they could get it right. There are still a few issues with the types as implemented in SQL Server 2008, but trust me, we are in much, much, much better shape now.
|
-
It is 2008. Not 1992. Why are we still intentionally creating filenames that conform to the limitations of ancient DOS and Windows 3.1 naming standards? This came up today in one of the SQL Server newsgroups, where a user was trying to find the data file for the Northwind database he was sure had just installed. Why couldn't he find it? Because he was searching for Northwind.mdf. Silly user! It's obviously going to be Northwnd.mdf. You should know that if i follows o, or if there are two e's, then you drop the second and all subsequent vowels to make an 8.3 filename. However, if you have a double o, two e's, or i before e, then you drop the first vowel that appears. *smacks forehead* If they couldn't call it Northwind.mdf because of the 8.3 limitation, then why didn't they pick a name they could still spell completely? Eastwind and Westwind come to mind. I wonder if I should file a suggestion on Connect to finally name the SQL Server executable correctly? I'm sure there are still people who try to find sqlserver.exe or sqlsrvr.exe or sqlsrver.exe...
| | |