0
Posted on Monday, October 20, 2014 by 醉·醉·鱼 and labeled under
源于一次数据库的面试题目,老板对别人的答题给了90分,我看了一下,没有看出理由。后来查了一下,原来如此。

简而言之,LEFT OUTER JOIN是先进行JOIN,拿出所有match的records,然后再做过滤。而NOT EXISTS是一旦有匹配就进行下一个比对。

这里有很详细的解释。
http://sqlinthewild.co.za/index.php/2010/03/23/left-outer-join-vs-not-exists/
http://stackoverflow.com/questions/6777910/sql-performance-on-left-outer-join-vs-not-exists

0
Posted on Monday, October 20, 2014 by 醉·醉·鱼 and labeled under
从SQL SERVER 2012开始,Alter table column from NULL to NOT NULL with default value is metadata change.

http://msdn.microsoft.com/en-us/library/ms190273.aspx

Adding NOT NULL Columns as an Online Operation

Starting with SQL Server 2012 Enterprise Edition, adding a NOT NULL column with a default value is an online operation when the default value is a runtime constant. This means that the operation is completed almost instantaneously regardless of the number of rows in the table. This is because the existing rows in the table are not updated during the operation; instead, the default value is stored only in the metadata of the table and the value is looked up as needed in queries that access these rows. This behavior is automatic; no additional syntax is required to implement the online operation beyond the ADD COLUMN syntax. A runtime constant is an expression that produces the same value at runtime for each row in the table regardless of its determinism. For example, the constant expression "My temporary data", or the system function GETUTCDATETIME() are runtime constants. In contrast, the functions NEWID() or NEWSEQUENTIALID() are not runtime constants because a unique value is produced for each row in the table. Adding a NOT NULL column with a default value that is not a runtime constant is always performed offline and an exclusive (SCH-M) lock is acquired for the duration of the operation.
除了文中说的例外,data compression也会有影响的。所以,还是得小心啊。
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/6784c3d8-2e40-47fd-956a-910c576a2266/changing-column-in-not-null-why-is-there-so-much-transaction-log-activity-on-compressed-tables?forum=sqldatabaseengine

此外,它依旧还是会有3个操作,所以http://dba.stackexchange.com/a/29526/37769 里面描述的依旧有效。

  1. 创建一个新的column
  2. 为新的column赋值
  3. 将前一个column标记dropped
0
Posted on Tuesday, September 02, 2014 by 醉·醉·鱼 and labeled under ,

问题

原始数据如下
需要在SSRS report里面呈现这样的结果。

解决


  1. 创建测试数据
    CREATE TABLE Test
    ([Student] varchar(1), [Class] varchar(8), [Score] int);

    INSERT INTO Test
    ([Student], [Class], [Score])
    VALUES
    ('A', 'English', 90),
    ('B', 'Computer', 78),
    ('B', 'Math', 88),
    ('C', 'Math', 55),
    ('C', 'Computer', 86),
    ('C', 'English', 64);
  2. 将数据按照student进行partition,按照class排序。
        select *, ROW_NUMBER() OVER (PARTITION BY Student ORDER BY Class) AS rn FROM Test
  3. 用这个query在SSRS里面添加一个新的dataset。
  4. 添加一个table,并添加Class和Score。
  5. 添加一个column group on Student。
  6. 添加一个row group on rn,并且添加一个group header。
  7. 清除rn column。因为没法隐藏,只能够把里面的内容清掉,然后去掉边框去掉。
  8. group header里面分别用表达式 =Min(Fields!Class.Value) & =Min(Fields!Score.Value)
  9. 最后把Details row隐藏起来。



PS: 我在Stackoverflow上面问了一圈,看上去都没有人解决。无意间看到MS官网,答案是说不可能。http://stackoverflow.com/questions/25602108/ssrs-how-to-create-pivot-table-result-without-blank-cells Stackoverflow也不是万能的啊。。。


0
Posted on Wednesday, August 27, 2014 by 醉·醉·鱼 and labeled under
早上例会提到了这么一个事情,大概知道老大说的什么,但是不是很明白。回来做了一个测试,用WHERE (@param IS NULL OR col = @param) 的确会比较糟糕。放狗一搜,文章不是很多,好在也能够找到几篇文章提到了这个。

这个文章算是比较全面的,里面提到了两个解决方案,都还不错。
http://sqlinthewild.co.za/index.php/2009/03/19/catch-all-queries/

问题

如下图所示,当用到WHERE (@param IS NULL OR col = @param) 的时候,会用到Index Scan,即使你在last_name上定义好了index。

解决方案

就现在而言,有两种解决方式。
1. OPTION(RECOMPILE)(SQL 2008 SP1/SP2/Later)


2. Dynamical SQL
可以改成
DECLARE @sql nvarchar(2000) = N'';

IF @param IS NOT NULL
SET @sql = @sql + 'select * from people p where p.last_name = @_lastName'

EXEC sys.sp_executesql @sql, N'@_lastName varchar(255)', @_lastName = @param


0
Posted on Tuesday, August 26, 2014 by 醉·醉·鱼 and labeled under ,
刚刚瞄了一眼http://www.sqlpassion.at/archive/2014/08/25/the-dangerous-beauty-of-the-pivot-operator-in-sql-server/?awt_l=KDBpM&awt_m=3eQDtEE5mzYUUTS 有点危言耸听了。其实里面就讲到一个东西,那就是pivot是如何去做grouping的。

借用http://blogs.msdn.com/b/spike/archive/2009/03/03/pivot-tables-in-sql-server-a-simple-sample.aspx 的例子。先创建如下table和数据。

create table DailyIncome(VendorId nvarchar(10), IncomeDay nvarchar(10), IncomeAmount int)
--drop table DailyIncome
insert into DailyIncome values ('SPIKE', 'FRI', 100)
insert into DailyIncome values ('SPIKE', 'MON', 300)
insert into DailyIncome values ('FREDS', 'SUN', 400)
insert into DailyIncome values ('SPIKE', 'WED', 500)
insert into DailyIncome values ('SPIKE', 'TUE', 200)
insert into DailyIncome values ('JOHNS', 'WED', 900)
insert into DailyIncome values ('SPIKE', 'FRI', 100)
insert into DailyIncome values ('JOHNS', 'MON', 300)
insert into DailyIncome values ('SPIKE', 'SUN', 400)
insert into DailyIncome values ('JOHNS', 'FRI', 300)
insert into DailyIncome values ('FREDS', 'TUE', 500)
insert into DailyIncome values ('FREDS', 'TUE', 200)
insert into DailyIncome values ('SPIKE', 'MON', 900)
insert into DailyIncome values ('FREDS', 'FRI', 900)
insert into DailyIncome values ('FREDS', 'MON', 500)
insert into DailyIncome values ('JOHNS', 'SUN', 600)
insert into DailyIncome values ('SPIKE', 'FRI', 300)
insert into DailyIncome values ('SPIKE', 'WED', 500)
insert into DailyIncome values ('SPIKE', 'FRI', 300)
insert into DailyIncome values ('JOHNS', 'THU', 800)
insert into DailyIncome values ('JOHNS', 'SAT', 800)
insert into DailyIncome values ('SPIKE', 'TUE', 100)
insert into DailyIncome values ('SPIKE', 'THU', 300)
insert into DailyIncome values ('FREDS', 'WED', 500)
insert into DailyIncome values ('SPIKE', 'SAT', 100)
insert into DailyIncome values ('FREDS', 'SAT', 500)
insert into DailyIncome values ('FREDS', 'THU', 800)
insert into DailyIncome values ('JOHNS', 'TUE', 600)

数据应该是这样的。
VendorId   IncomeDay  IncomeAmount
---------- ---------- ------------
SPIKE      FRI        100
SPIKE      MON        300
FREDS      SUN        400
SPIKE      WED        500
SPIKE      TUE        200
JOHNS      WED        900
SPIKE      FRI        100
JOHNS      MON        300
SPIKE      SUN        400
...
SPIKE      WED        500
FREDS      THU        800
JOHNS      TUE        600

执行如下query,就可以进行一次简单的pivot操作。
select * from DailyIncome
pivot (avg (IncomeAmount) for IncomeDay in ([MON],[TUE],[WED],[THU],[FRI],[SAT],[SUN])) as AvgIncomePerDay

但是,如果,这个时候DailyIncome table多增加了一个column,那情况就不会是这样的了。像http://www.sqlpassion.at/archive/2014/08/25/the-dangerous-beauty-of-the-pivot-operator-in-sql-server/?awt_l=KDBpM&awt_m=3eQDtEE5mzYUUTS 说的那样,得到的结果就不会像想象中的那样了。原因就在于pivot里面会用除了IncomeDay和IncomeAmount的column进行group操作,进而导致了pivot的结果显示的乱糟糟的。

解决方法

将DailyIncome换成Table express,即
select * from (select VendorId, IncomeDay, IncomeAmount from DailyIncome) AS t
pivot (avg (IncomeAmount) for IncomeDay in ([MON],[TUE],[WED],[THU],[FRI],[SAT],[SUN])) as AvgIncomePerDay

这个问题可能会出现的比较隐蔽些,因为table的改动很难想到会影响pivot操作。

0
Posted on Monday, August 25, 2014 by 醉·醉·鱼 and labeled under ,
原文在这里http://www.sqlpassion.at/archive/2013/06/12/sql-server-tipping-games-why-non-clustered-indexes-are-just-ignored/,我顺带翻译成中文吧。

有时候,SQL SERVER会忽略掉定义好的Non-Clustered Index,而选用Clustered Index Scan,这种行为在SQL SERVER里面叫做‘Tipping Point’。 而且,最重要的是,这个Tipping Point和Table Page有关,而和Table records无关。Tipping point大概在24%-33%之间。

开始如下测试。创建一个Table,每条记录会有40bytes,每个page会有200条记录。


插入80000条记录。所以会有400个page。所以Tipping point大概是在100 - 133 之间(实际上这个测试里面会高于这个值),那相当于说,用NonClustered Index的话,你只能够取到这张表0.125% - 0.167%的记录。这相当于还不用这个Index。

实际上这次的Tipping Point会在158。如下,第一个query会用到NonClustered Index,而第二个query直接用Clustered Index Scan了。

执行计划

更加有趣的是,如果我强制使用NonClustered Index,下面的这段query将会达到165148!这可是只有420page的table啊!



0
Posted on Sunday, August 24, 2014 by 醉·醉·鱼 and labeled under