블로그 이미지
010-9967-0955 보미아빠

카테고리

보미아빠, 석이 (500)
밥벌이 (16)
싸이클 (1)
일상 (1)
Total
Today
Yesterday

달력

« » 2024.4
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30

공지사항

최근에 올라온 글

7월 21일 엄마는 병원 입니다.
곽생로~ 산부인과 -_- 엄마가 이렇게 많은곳은 처음 입니다. 열라 많아요~


여름이 누나 근심이 가득해요....-_- /
오렌지 주스 한잔 마시면서 ........여름이 기다리는 보미 누나~

드뎌 7월 21일 3시 35분 여름이가 나왔습니다. ^^;; 제가 첫 목욕을 시켜 줬습니다. ^^~~~

보미 누나가 여름이 안고 좋아라 하네요~ 넘 이뻐요 여름이 누님~


모두에게 감사드립니다.
수고한 울 마눌이 젤 감사하구....내가 명품 빽!! 하나 질러줄께........~ ^.^ 좀만 기달료~ 성과급 곧 들어올끼야...ㅋㅋ

우리 4인 가족 열심히 살자구 ~ 화이팅~
보미 아빠
여름이 아빠
은깽이 남편~
Posted by 보미아빠
, |

예전 김평철 CTO님 외 SQL Server 쪽에 한국인이 개입한 적이 없는걸로 안다.
이런분이 많이 나오면 한글로 좀 깊이있게 배울수 있지 않을까.....
하루 4시간은 영어만 쳐다본다. 이게 고마운건지 불행한건지 모르겠다. 자야지.....휴~

코피~ 아흑~

Posted by 보미아빠
, |



Licensing advantages. Server consolidation can also have a significant impact on software
costs by reducing the number of licenses required. With Windows Server 2008 Standard Edition,
three virtual machines can be hosted on one physical server without the need to purchase
additional licenses
. With the Enterprise Edition, an unlimited number of virtual machines can be
hosted on one physical server without the need to purchase additional licenses
. The Enterprise
Edition also provides the ability to reassign licenses in a server farm.

이런말이 있는데......잘 함 봐야 할 듯.....음 windows license 정책을 봐야 할듯
못 찾겠다.......구라친듯..-_-

http://www.microsoft.com/windowsserver2008/en/us/licensing-faq.aspx#virt
Posted by 보미아빠
, |
아래 그림을 많이 보았겠지요?
아래 그림은 Gartner 에서 주로 쓰는 Magic Quardrants 입니다.
유료 자료이고 매우 신뢰성이 높다는 특징이 있습니다.


http://www.gartner.com/technology/research/methodologies/magicQuadrants.jsp

여기서 제목을 찾고 pdf 를 google 신께 굽신 거리면 됩니다. ^^; 어렵게 살면 이렇게 밖에는 쿨럭~
예를 들어 Magic Quadrant for x86 Server Virtualization Infrastructure 를 보고 싶다면 google 에서 찾으면 나오지요


http://www.vmware.com/files/pdf/cloud/Gartner-VMware-Magic-Quadrant.pdf
리포트에서 보면 2010 년까지는 VMware 가 1등 이네요. 타 업체는 저 아래 있군요

 




http://www.citrix.com/site/resources/dynamic/additional/citirix_magic_quadrant_2011.pdf
자료를 보면 Citrix Systems 가 Leaders 그룹에 겨우 찡겼군요....그래서 citirix 가 공짜로 돌립니다.



음 그냥 VMware 로 가야 하는군요......-_- Novell 은 나가 떨어졌나보군요 -_-
Microsoft 는 돈은 있는데 기술력이 후달리지만 리더로 올라는 갔군요. 살아 남을려고 발버둥 치는 모습이 보입니다.

세상은 냉정하니까요~ -_- 평가도 냉정하고... 공부해야지......으쌰 으쌰~

Posted by 보미아빠
, |

CTE

카테고리 없음 / 2011. 7. 18. 03:00
Common Table Expression 

타 데이터베이스의 경우, CTE내용을 쿼리에서 여러번 쓰일경우 실체화 해 한번만 CTE 연산을 하고, 나머지 쿼리에서는 그 결과를 계속 사용해 퀴리를 최적화 하게 된다. 그러나, SQL Server 의 경우 그러한 최적화는 수행하지 못한다. 
(-_- 아~ 꼬질 꼬질 븅신 -_- 들 떨어진...... 드날려보는 2011?은 해결되면 좋으련만 현재 2008 r2 까지는 수행하지 못한다.) 

문론, 재사용을 위해서는 내부적으로 Temp 테이블 등을 이용해 실체화 시켜야 한다. 이러한 DDL 비용이나 Temp를 사용하는 비용이 CTE를 여러번 참조해서 실제로 쿼리하는 비용보다 비쌀 수 도 있다. 하지만 반대의 경우도 있고 다른 DBMS는 지원해준다는 것이 바보 스럽다는 이유이다.  내 생각에 재귀 쿼리를 지원하기 위해 CTE를 만든 것이지 이러한 최적화를 위해서 만든것은 아닌듯 하다.

SQL Server 에서의 CTE는 쿼리를 알아보기 쉽게, 또는 쿼리 자체를 좀 심플하게 적는것 외 도움 되는것이 없다.
 
그러나, predicate merge 기능은 지원한다. CTE 가 테이블 전체를 대상으로 하더라도 외부의 where predicate 가 CTE 내부로 들어가 필터 할 수 있다.  이것도 merge or push predicate 와 마찬가지로 top iterator 등으로 이러한 현상을 막을 수 있다.









Posted by 보미아빠
, |

음 책 검수해야 하는데......
마음 다잡고 다시 으쌰 으쌰~ 

 



if object_id('tblx') is not null
 drop table tblx
 go
 
create table tblx
 (cval char(1)
 ,x int
 ,y int
 )
 go
 
 --필요한 dot design 노가다
 insert tblx values ('1',1,3) , ('1',2,2) , ('1',2,3) , ('1',3,3) , ('1',4,3) , ('1',5,3) , ('1',6,3) , ('1',7,3)
 , ('2',1,2) , ('2',1,3) , ('2',2,1) , ('2',2,4) , ('2',3,4) , ('2',4,2) , ('2',4,3) , ('2',5,1) , ('2',6,1) , ('2',7,1) , ('2',7,2) , ('2',7,3) , ('2',7,4)
 , ('3',1,2) , ('3',1,3) , ('3',2,1) , ('3',2,4) , ('3',3,4) , ('3',4,2) , ('3',4,3) , ('3',5,4) , ('3',6,1) , ('3',6,4) , ('3',7,2) , ('3',7,3)
 , ('4',1,3) , ('4',2,3) , ('4',3,2) , ('4',3,3) , ('4',4,1) , ('4',4,3) , ('4',5,1) , ('4',5,2) , ('4',5,3) , ('4',5,4) , ('4',6,3) , ('4',7,3)
 , ('5',1,1) , ('5',1,2) , ('5',1,3) , ('5',1,4) , ('5',2,1) , ('5',3,1) , ('5',4,1) , ('5',4,2) , ('5',4,3) , ('5',5,4) , ('5',6,4) , ('5',7,1) , ('5',7,2) , ('5',7,3)
 , ('6',1,2) , ('6',1,3) , ('6',2,1) , ('6',2,4) , ('6',3,1) , ('6',4,1) , ('6',4,2) , ('6',4,3) , ('6',5,1) , ('6',5,4) , ('6',6,1) , ('6',6,4) , ('6',7,2) , ('6',7,3)
 , ('7',1,1) , ('7',1,2) , ('7',1,3) , ('7',1,4) , ('7',2,1) , ('7',2,4) , ('7',3,4) , ('7',4,4) , ('7',5,4) , ('7',6,4) , ('7',7,4)
 , ('8',1,2) , ('8',1,3) , ('8',2,1) , ('8',2,4) , ('8',3,1) , ('8',3,4) , ('8',4,2) , ('8',4,3) , ('8',5,1) , ('8',5,4) , ('8',6,1) , ('8',6,4) , ('8',7,2) , ('8',7,3)
 , ('9',1,2) , ('9',1,3) , ('9',2,1) , ('9',2,4) , ('9',3,1) , ('9',3,4) , ('9',4,2) , ('9',4,3) , ('9',4,4) , ('9',5,4) , ('9',6,1) , ('9',6,4) , ('9',7,2) , ('9',7,3)
 , ('0',1,2) , ('0',1,3) , ('0',2,1) , ('0',2,4) , ('0',3,1) , ('0',3,4) , ('0',4,1) , ('0',4,4) , ('0',5,1) , ('0',5,4) , ('0',6,1) , ('0',6,4) , ('0',7,2) , ('0',7,3)
 , ('-',4,1) , ('-',4,2)
 , (':',2,2) , (':',6,2)

 

-- 필요한 컬럼만큼 출력
 select max(case when cval = substring(num,1,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,1,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,1,1) and y = 3 then '■' else '' end)a
   , max(case when cval = substring(num,1,1) and y = 4 then '■' else '' end)a
   , max(case when cval = substring(num,2,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,2,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,2,1) and y = 3 then '■' else '' end)a
   , max(case when cval = substring(num,2,1) and y = 4 then '■' else '' end)a
   , max(case when cval = substring(num,3,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,3,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,3,1) and y = 3 then '■' else '' end)a
   , max(case when cval = substring(num,3,1) and y = 4 then '■' else '' end)a
   , max(case when cval = substring(num,4,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,4,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,4,1) and y = 3 then '■' else '' end)a
   , max(case when cval = substring(num,4,1) and y = 4 then '■' else '' end)a
   , max(case when cval = substring(num,5,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,5,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,6,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,6,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,6,1) and y = 3 then '■' else '' end)a
   , max(case when cval = substring(num,6,1) and y = 4 then '■' else '' end)a
   , max(case when cval = substring(num,7,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,7,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,7,1) and y = 3 then '■' else '' end)a
   , max(case when cval = substring(num,7,1) and y = 4 then '■' else '' end)a
   , max(case when cval = substring(num,8,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,8,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,9,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,9,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,9,1) and y = 3 then '■' else '' end)a
   , max(case when cval = substring(num,9,1) and y = 4 then '■' else '' end)a
   , max(case when cval = substring(num,10,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,10,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,10,1) and y = 3 then '■' else '' end)a
   , max(case when cval = substring(num,10,1) and y = 4 then '■' else '' end)a
   , max(case when cval = substring(num,11,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,11,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,12,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,12,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,12,1) and y = 3 then '■' else '' end)a
   , max(case when cval = substring(num,12,1) and y = 4 then '■' else '' end)a
   , max(case when cval = substring(num,13,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,13,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,13,1) and y = 3 then '■' else '' end)a
   , max(case when cval = substring(num,13,1) and y = 4 then '■' else '' end)a
   , max(case when cval = substring(num,14,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,14,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,14,1) and y = 3 then '■' else '' end)a
   , max(case when cval = substring(num,15,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,15,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,15,1) and y = 3 then '■' else '' end)a
   , max(case when cval = substring(num,15,1) and y = 4 then '■' else '' end)a
   , max(case when cval = substring(num,16,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,16,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,16,1) and y = 3 then '■' else '' end)a
   , max(case when cval = substring(num,16,1) and y = 4 then '■' else '' end)a
   , max(case when cval = substring(num,17,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,17,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,17,1) and y = 3 then '■' else '' end)a
   , max(case when cval = substring(num,18,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,18,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,18,1) and y = 3 then '■' else '' end)a
   , max(case when cval = substring(num,18,1) and y = 4 then '■' else '' end)a
   , max(case when cval = substring(num,19,1) and y = 1 then '■' else '' end)a
   , max(case when cval = substring(num,19,1) and y = 2 then '■' else '' end)a
   , max(case when cval = substring(num,19,1) and y = 3 then '■' else '' end)a
   , max(case when cval = substring(num,19,1) and y = 4 then '■' else '' end)a
   from tblx a
  cross join (select convert(varchar(20), getdate(), 120) num) b -- 도트에 있는 출력할 문자열 입력
  group by x
  order by x


 

Posted by 보미아빠
, |
문제 재현을 질문 하실때, 일일이 설명하기 귀찮을 경우가 많을 것입니다.
windows blog (sankim) 를 보다가 좋은 아티를을 발견 했습니다.

psr.exe 를 이용해 문재 재현을 쉽게 레코딩 및 설명 할 수 있습니다.

첨부파일은 시작>실행 psr.exe 를 통해서 에러를 다시 나게하는 것을 주욱~ capture 한 것입니다.

녹화시작 하고 죽 ~ 녹화 하다가 comment 를 추가 시키고 싶으면 comment 버튼을 누르고 결과를 캡쳐 하시면 됩니다.
슬라이드쇼로도 볼 수 있고 아주 유용한 툴인듯 합니다.
소소한 것은 이걸로 manual 을 만들수도 있을듯 합니다.





Posted by 보미아빠
, |
많은분이 참석 하셨네요. 좋은 시간이 되었을라나 모르겠습니다.




질문은 저분에게 하세요 ^^; 농담이구요 올려주시면 최대한 답변 해보도록 하겠습니다.
앞으로도 스터디 쭈욱 나오실거죠?
역쉬 젊은피가 많이 들어오니 활력이 넘칩니다.

감사합니다.

from a
sqlsql.tistory.com
sqltag.org

P.S
숙봉~ 대단해. 화이팅!
교성이는 역쉬 똑똑해~
성욱아~ 고마워~
마지막으로 책 쓰시는 분들 대단해요 책 나오면 쓴사람들 다 모여서 거사를 치르자구요. 조금만 고생하자!
신입들! 정신 바딱 차리고 열심히 합시다.

Posted by 보미아빠
, |

i/o

카테고리 없음 / 2011. 7. 8. 02:49


In this post from last year, I discussed how random I/Os are slower than sequential I/Os (particularly for conventional rotating hard drives).  For this reason, SQL Server often favors query plans that perform sequential scans of an entire table over plans that perform random lookups of only a portion of a table.  (See the last example in this post for a simple demonstration.)  In other cases, instead of performing a sequential scan, SQL Server introduces a sort operator whose sole purpose is to convert random I/Os into sequential I/Os.
Let's look at an example of such a sort.  To measure the performance effects, we'll need a reasonably large table.  The following script creates a 25.6 million row table that consumes about 3 GBytes of storage.

CREATE DATABASE IOTest
    ON ( NAME = IOTest_Data, FILENAME = '...\IOTest_Data.mdf', SIZE = 4 GB )
    LOG ON ( NAME = IOTest_Log, FILENAME = '...\IOTest_Log.ldf', SIZE = 200 MB )
GO
ALTER DATABASE IOTest SET RECOVERY SIMPLE
GO
USE IOTest
GO
CREATE TABLE T (
    PK INT IDENTITY PRIMARY KEY,
    RandKey INT,
    Flags TINYINT,
    Data INT,
    Pad CHAR(100))
GO
SET NOCOUNT ON
DECLARE @I INT
SET @I = 0
WHILE @I < 100000
  BEGIN
    WITH
      X2 (R) AS ( SELECT RAND() UNION ALL SELECT RAND() ),
      X4 (R) AS ( SELECT R FROM X2 UNION ALL SELECT R FROM X2 ),
      X8 (R) AS ( SELECT R FROM X4 UNION ALL SELECT R FROM X4 ),
      X16 (R) AS ( SELECT R FROM X8 UNION ALL SELECT R FROM X8 ),
      X32 (R) AS ( SELECT R FROM X16 UNION ALL SELECT R FROM X16 ),
      X64 (R) AS ( SELECT R FROM X32 UNION ALL SELECT R FROM X32 ),
      X128 (R) AS ( SELECT R FROM X64 UNION ALL SELECT R FROM X64 ),
      X256 (R) AS ( SELECT R FROM X128 UNION ALL SELECT R FROM X128 )
    INSERT T (RandKey, Flags, Data, Pad)
        SELECT R * 1000000000, 0xFF, 1, '' FROM X256
    SET @I = @I + 1
  END
GO
CREATE INDEX IRandKey on T (RandKey, Flags)
GO

Due to the fixed width Pad column, each row of T consumes 113 bytes (plus overhead).  Roughly 65 rows fit on a single 8 Kbyte page.  (The Flags column is unused in this example, but I will make use of it in a subsequent post.)

The RandKey column, as the name suggests, contains random values.  Notice that we have a non-clustered index on this column.  Given a predicate on the RandKey column, SQL Server can use this index to fetch qualifying rows from the table.  However, because the values in this column are random, the selected rows will be scattered randomly throughout the clustered index.

If we select just a few rows from the table using a filter on RandKey, SQL Server will use the non-clustered index:

SELECT SUM(Data)
FROM T
WHERE RandKey < 1000

  |--Compute Scalar(DEFINE:([Expr1003]=CASE WHEN [Expr1011]=(0) THEN NULL ELSE [Expr1012] END))
       |--Stream Aggregate(DEFINE:([Expr1011]=COUNT_BIG([T].[Data]), [Expr1012]=SUM([T].[Data])))
            |--Nested Loops(Inner Join, OUTER REFERENCES:([T].[PK], [Expr1010]) OPTIMIZED WITH UNORDERED PREFETCH)
                 |--Index Seek(OBJECT:([T].[IRandKey]), SEEK:([T].[RandKey] < (1000)) ORDERED FORWARD)
                 |--Clustered Index Seek(OBJECT:([T].[PK__T__...]), SEEK:([T].[PK]=[T].[PK]) LOOKUP ORDERED FORWARD)

The non-clustered index seek selects a few rows (the use of random keys means that the exact number may vary each time the table is loaded) and looks them up in the clustered index to get the value of the Data column for the SUM aggregate.  The non-clustered index seek is very efficient - it likely touches only one page - but the clustered index seek generates a random I/O for each row.

If we select a large number of rows, SQL Server recognizes that the random I/Os are too expensive and switches to a clustered index scan:

SELECT SUM(Data)
FROM T
WHERE RandKey < 10000000

  |--Compute Scalar(DEFINE:([Expr1003]=CASE WHEN [Expr1009]=(0) THEN NULL ELSE [Expr1010] END))
       |--Stream Aggregate(DEFINE:([Expr1009]=COUNT_BIG([T].[Data]), [Expr1010]=SUM([T].[Data])))
            |--Clustered Index Scan(OBJECT:([T].[PK__T__...]), WHERE:([T].[RandKey]<(10000000)))

This query touches only 1% of the data.  Still, the query is going to touch more than half of the pages in the clustered index so it is faster to scan the entire clustered index than to perform on the order of 256,000 random I/Os.

Somewhere in between these two extremes things get a little more interesting:

SELECT SUM(Data)
FROM T
WHERE RandKey < 2500000

  |--Compute Scalar(DEFINE:([Expr1003]=CASE WHEN [Expr1010]=(0) THEN NULL ELSE [Expr1011] END))
       |--Stream Aggregate(DEFINE:([Expr1010]=COUNT_BIG([T].[Data]), [Expr1011]=SUM([T].[Data])))
            |--Nested Loops(Inner Join, OUTER REFERENCES:([T].[PK], [Expr1009]) WITH UNORDERED PREFETCH)
                 |--Sort(ORDER BY:([T].[PK] ASC))
                 |    |--Index Seek(OBJECT:([T].[IRandKey]), SEEK:([T].[RandKey] < (2500000)) ORDERED FORWARD)
                 |--Clustered Index Seek(OBJECT:([T].[PK__T__...]), SEEK:([T].[PK]=[T].[PK]) LOOKUP ORDERED FORWARD)

This query touches a mere 0.25% of the data.  The plan uses the non-clustered index to avoid unnecessarily touching many rows.  Yet, performing 64,000 random I/Os is still rather expensive so SQL Server adds a sort.  By sorting the rows on the clustered index key, SQL Server transforms the random I/Os into sequential I/Os.  Thus, we get the efficiency of the seek - touching only those rows that qualify - with the performance of the sequential scan.

It is worth pointing out that sorting on the clustered index key will yield rows that are in the logical index order.  Due to fragmentation or due simply to the multiple layers of abstraction between SQL Server and the actual hard drives, there is no guarantee that the physical order on disk matches the logical order.

In my next post, I'll run some of these queries and demonstrate the performance implications of the sort.

 

 

 

 

 

 

 

 

 


In my last post, I discussed how SQL Server can use sorts to transform random I/Os into sequential I/Os.  In this post, I'll demonstrate directly how such a sort can impact performance.  For the following experiments, I'll use the same 3 GByte database that I created last week.

The system I'm using to run this test has 8 GBytes of memory.  To exaggerate the performance effects and simulate an even larger table that does not fit in main memory, I'm going to adjust the ‘MAX SERVER MEMORY' SP_CONFIGURE option to allow SQL Server to use just 1 GByte of memory.  I'm going to use CHECKPOINT to ensure that the newly created database is completely flushed to disk before running any experiments.  Finally, I'm going to run DBCC DROPCLEANBUFFERS before each test to ensure that none of the data is cached in the buffer pool between tests.

CHECKPOINT

EXEC SP_CONFIGURE 'SHOW ADVANCED OPTIONS', '1'
RECONFIGURE
EXEC SP_CONFIGURE 'MAX SERVER MEMORY', '1024'
RECONFIGURE

DBCC DROPCLEANBUFFERS

Note that you will NOT want to run these statements on a production server.

As I discussed last week, SQL Server can use one of three plans for the following query depending on the value of the constant:

SELECT SUM(Data)
FROM T
WHERE RandKey < constant

To recap, if the constant is small, SQL Server uses a non-clustered index seek and a bookmark lookup.  If the constant is large, SQL Server uses a clustered index scan to avoid performing many random I/Os.  Finally,  if the constant is somewhere in the middle, SQL Server uses the non-clustered index seek but sorts the rows prior to performing the bookmark lookup to reduce the number of random I/Os.  You can review last week's post to see examples of each of these plans.  I'm going to focus on the third and final plan with the sort.

To demonstrate the benefit of the sort, I need to be able to run the same query with and without the sort.  A simple way to make SQL Server remove the sort is to use the following UPDATE STATISTICS statement to trick SQL Server into believing that the table is really small.  To ensure that I still get the plan with the non-clustered index seek and the bookmark lookup, I need to add an INDEX hint.  I'm also adding a RECOMPILE query hint to ensure that SQL Server generates a new plan after I've altered the statistics.

UPDATE STATISTICS T WITH ROWCOUNT = 1, PAGECOUNT = 1

SELECT SUM(Data)
FROM T WITH (INDEX (IRandKey))
WHERE RandKey < constant
OPTION (RECOMPILE)

I can also reset the statistics using the following statement:

UPDATE STATISTICS T WITH ROWCOUNT = 25600000, PAGECOUNT = 389323

Here is an example of the default plan with the real statistics and with the sort:

  |--Compute Scalar(DEFINE:([Expr1003]=CASE WHEN [Expr1010]=(0) THEN NULL ELSE [Expr1011] END))
       |--Stream Aggregate(DEFINE:([Expr1010]=COUNT_BIG([T].[Data]), [Expr1011]=SUM([T].[Data])))
            |--Nested Loops(Inner Join, OUTER REFERENCES:([T].[PK], [Expr1009]) WITH UNORDERED PREFETCH)
                 |--Sort(ORDER BY:([T].[PK] ASC))
                 |    |--Index Seek(OBJECT:([T].[IRandKey]), SEEK:([T].[RandKey] < (2000000)) ORDERED FORWARD)
                 |--Clustered Index Seek(OBJECT:([T].[PK__T__...]), SEEK:([T].[PK]=[T].[PK]) LOOKUP ORDERED FORWARD)

Here is an example of the plan after running UPDATE STATISTICS and without the sort:

  |--Compute Scalar(DEFINE:([Expr1003]=CASE WHEN [Expr1009]=(0) THEN NULL ELSE [Expr1010] END))
       |--Stream Aggregate(DEFINE:([Expr1009]=COUNT_BIG([T].[Data]), [Expr1010]=SUM([T].[Data])))
            |--Nested Loops(Inner Join, OUTER REFERENCES:([T].[PK]))
                 |--Index Seek(OBJECT:([T].[IRandKey]), SEEK:([T].[RandKey] < (2000000)) ORDERED FORWARD)
                 |--Clustered Index Seek(OBJECT:([T].[PK__T__...]), SEEK:([T].[PK]=[T].[PK]) LOOKUP ORDERED FORWARD)

Here are my results running this query with two values of the constant both with and without the sort.  Keep in mind that these results depend greatly on the specific hardware.  If you try this experiment, your results may vary.

 
 Execution Time
 % Increase
 
with Sort
 without Sort
 
Constant
 2,000,000
(0.2% of rows)
 91 seconds
 352 seconds
 286%
 
4,000,000
(0.4% of rows)
 97 seconds
 654 seconds
 574%
 
% Increase
 100%
 6%
 86%
 
 

There are a two points worth noting regarding these results.  First, it should be very clear that the plan with the sort is significantly faster (up to 7 times faster) than the plan without the sort.  This result clearly shows the benefit of sequential vs. random I/Os.  Second, doubling the number of rows touched had hardly any effect on the execution time for the plan with the sort but nearly doubled the execution time for the plan without the sort.  Adding additional I/Os to the plan with the sort adds only a small incremental cost since the I/Os are sequential and the disk head will pass over the required data exactly once either way.  Adding additional I/Os to the plan without the sort adds additional disk seeks and increases the execution time proportionately to the increase in the number of rows.  In fact, if the constant is increased further, the execution time of the plan with the sort will continue to increase only gradually with the execution time of the plan without the sort will continue to increase rapidly.

 

 

 

 

 

 

 

In my past two posts, I explained how SQL Server may add a sort to the outer side of a nested loops join and showed how this sort can significantly improve performance.  In an earlier post, I discussed how SQL Server can use random prefetching to improve the performance of a nested loops join.  In this post, I'm going to explore one more nested loops join performance feature.  I'll use the same database that I used in my two prior posts.  Let's start with the following simple query:
SELECT SUM(Data)
FROM T
WHERE RandKey < 1000

  |--Compute Scalar(DEFINE:([Expr1003]=CASE WHEN [Expr1011]=(0) THEN NULL ELSE [Expr1012] END))
       |--Stream Aggregate(DEFINE:([Expr1011]=COUNT_BIG([T].[Data]), [Expr1012]=SUM([T].[Data])))
            |--Nested Loops(Inner Join, OUTER REFERENCES:([T].[PK], [Expr1010]) OPTIMIZED WITH UNORDERED PREFETCH)
                 |--Index Seek(OBJECT:([T].[IRandKey]), SEEK:([T].[RandKey] < (1000)) ORDERED FORWARD)
                 |--Clustered Index Seek(OBJECT:([T].[PK__T__...]), SEEK:([T].[PK]=[T].[PK]) LOOKUP ORDERED FORWARD)

Notice that the nested loops join includes an extra keyword: OPTIMIZED.  This keyword indicates that the nested loops join may try to reorder the input rows to improve I/O performance.  This behavior is similar to the explicit sorts that we saw in my two previous posts, but unlike a full sort it is more of a best effort.  That is, the results from an optimized nested loops join may not be (and in fact are highly unlikely to be) fully sorted.

SQL Server only uses an optimized nested loops join when the optimizer concludes based on its cardinality and cost estimates that a sort is most likely not required, but where there is still a possibility   that a sort could be helpful in the event that the cardinality or cost estimates are incorrect.  In other words, an optimized nested loops join may be thought of as a "safety net" for those cases where SQL Server chooses a nested loops join but would have done better to have chosen an alternative plan such as a full scan or a nested loops join with an explicit sort.  For the above query which only joins a few rows, the optimization is unlikely to have any impact at all.

Let's look at an example where the optimization actually helps:

SELECT SUM(Data)
FROM T
WHERE RandKey < 100000000 AND
    Flags & 0x1 = 0x1 AND
    Flags & 0x2 = 0x2 AND
    Flags & 0x4 = 0x4 AND
    Flags & 0x8 = 0x8

  |--Compute Scalar(DEFINE:([Expr1003]=CASE WHEN [Expr1014]=(0) THEN NULL ELSE [Expr1015] END))
       |--Stream Aggregate(DEFINE:([Expr1014]=COUNT_BIG([T].[Data]), [Expr1015]=SUM([T].[Data])))
            |--Nested Loops(Inner Join, OUTER REFERENCES:([T].[PK], [Expr1013]) OPTIMIZED WITH UNORDERED PREFETCH)
                 |--Index Seek(OBJECT:([T].[IRandKey]), SEEK:([T].[RandKey] < (100000000)),  WHERE:(([T].[Flags]&(1))=(1) AND ([T].[Flags]&(2))=(2) AND ([T].[Flags]&(4))=(4) AND ([T].[Flags]&(8))=(8)) ORDERED FORWARD)
                 |--Clustered Index Seek(OBJECT:([T].[PK__T__...]), SEEK:([T].[PK]=[T].[PK]) LOOKUP ORDERED FORWARD)

The Flags column contains the value 0xFF in every row.  Thus, every one of the bitwise AND predicates evaluates to true and this query returns about 2.5 million rows or 10% of the table.  Ordinarily, when faced with a query like this one, SQL Server would resort to a sequential scan of the entire table.  Indeed, if you try this query without the extra bitwise filters, you will get a sequential scan.  However, SQL Server does not realize that these predicates are always true, estimates a much lower cardinality of less than 10,000 rows, and chooses a simple nested loops join plan.  Note that I would generally recommend against using predicates like these ones in a real world application precisely because they will lead to cardinality estimation errors and poor plans.

To see what effect the optimized nested loops join has, let's compare the above plan with an "un-optimized" nested loops join.  We can eliminate the optimization by using the following UPDATE STATISTICS statement to trick SQL Server into believing that the table is very small:

UPDATE STATISTICS T WITH ROWCOUNT = 1, PAGECOUNT = 1

I'll compare the above query with the following simpler query which uses essentially the same plan and touches the same data but has an "un-optimized" nested loops join:

SELECT SUM(Data)
FROM T WITH (INDEX (IRandKey))
WHERE RandKey < 100000000

  |--Compute Scalar(DEFINE:([Expr1003]=CASE WHEN [Expr1009]=(0) THEN NULL ELSE [Expr1010] END))
       |--Stream Aggregate(DEFINE:([Expr1009]=COUNT_BIG([T].[Data]), [Expr1010]=SUM([T].[Data])))
            |--Nested Loops(Inner Join, OUTER REFERENCES:([T].[PK]))
                 |--Index Seek(OBJECT:([T].[IRandKey]), SEEK:([T].[RandKey] < (100000000)) ORDERED FORWARD)
                 |--Clustered Index Seek(OBJECT:([T].[PK__T__...]), SEEK:([T].[PK]=[T].[PK]) LOOKUP ORDERED FORWARD)

We can reset the statistics using the following statement:

UPDATE STATISTICS T WITH ROWCOUNT = 25600000, PAGECOUNT = 389323

As in my last post, I'm going to simulate a larger table by reducing the memory available to the server to 1 GByte with SP_CONFIGURE 'MAX SERVER MEMORY' and I'm also going to flush the buffer pool between runs with DBCC DROPCLEANBUFFERS.

Note that you will NOT want to run these statements on a production server.

I ran both of the above queries with three different constants.  Here are my results.  Keep in mind that these results depend greatly on the specific hardware.  If you try this experiment, your results may vary.

 
 Execution Time
 Increase
 
OPTIMIZED
 "un-OPTIMIZED"
 
Constant
 10,000,000
(1% of rows)
 6.5 minutes
 26 minutes
 4x
 
100,000,000
(10% of rows)
 10.4 minutes
 4.3 hours
 25x
 
250,000,000
(25% of rows)
 11.3 minutes
 10.6 hours
 56x
 

Clearly the optimized nested loops join can have a huge impact on performance.  Moreover, as the plan touches more rows the benefit of the optimization grows dramatically.  Although a full scan or a nested loops join with an explicit sort would be faster, the optimized nested loops join really is a safety net protecting against a much worse alternative.

Posted by 보미아빠
, |
아래 그림의 붉은색 부분이 null 로 나올때



C:\Program Files (x86)\Microsoft SQL Server\100\Tools\Profiler\Templates\Microsoft SQL Server
에 있는 버전별 템플릿을 카피해 넣는다.
안되는 서버에서 해보면 될듯 하여간 processMonitor 에서 보니 해당 폴더의 버전별로 모든 tdf 파일을 당기는 것을 볼 수 있다.

수동으로 만들려면, 있는 서버에서 저장하고
profiler > 파일 > 템플릿 > 템플릿 가져오기를 한다.

왜 지워졌을까?......-_- 궁금하네....
아마 2008 만 깔려있는 서버에서 2008 r2 의 템플릿을 부를려고 해서 에러난것 같다.

Posted by 보미아빠
, |

최근에 달린 댓글

최근에 받은 트랙백

글 보관함