퇴근5분전

세번째 풀이

sqler를 들려보니 각 일별로 분을 만든게 떠있네? 음...

그래서 다시 만들어봤다. 1시간 소요 ㅠㅠ... 만들어 놓은걸 안보고 해봤음. ( 보고 싶은 유혹이... )

역시나 아직 내가 놓치는 부분들이 있네.

 

 

;with ttb as
(
  select DATEADD( HOUR, -8, convert( datetime, st )) as st, DATEADD( HOUR, -8, convert( datetime, ed)) as ed    
  from
  (
    values
     ( '2016-05-09 09:00:00', '2016-05-09 09:30:00' ),
      ( '2016-05-09 23:00:00', '2016-05-10 09:00:00' ),
      ( '2016-05-10 22:20:00', '2016-05-11 05:00:00' ),  -- 테스트를 위해 추가.
     ( '2016-05-12 22:20:00', '2016-05-15 05:00:00' )  -- 테스트를 위해 추가.
  ) as tb ( st, ed)
 /* 문제 :
  이와같은 데이터가 있을때 합산하는 시점은 오전 08시로 기준을 잡고
  2016-05-09 : 570분    (9:00 ~ 09:30 = 30분 + 23:00 ~ 익일 08:00 = 540분)
  2016-05-10 : 60분     (기준시간 08:00~ 09:00 = 60분)
  이렇게 SELECT 를 하고싶은데요.. 너무헷갈립니다 고수분들 조언부탁 드립니다.
 */
), otb ( st, ed )
as
(
 select DATEADD( hour, -8, convert( datetime, st )) as st,
        DATEADD( hour, -8, convert( datetime, ed )) as ed
   from ttb 
)
, rtb ( diff, dt )
as
(
 select DATEDIFF( day, min( st ), max( ed ) ) as diff, convert( datetime, convert( varchar(10), min( st ), 121 )) as dt
   from otb
)
, jtb ( dt1 , dt2)
as
(
 select DATEADD( Day, number, dt ) as dt1, dateadd( MILLISECOND, -2, DATEADD( Day, number + 1, dt )) as dt2
 from rtb, (
    select number
    from master.dbo.spt_values
    where name is null ) as ntb
 where ntb.number <= rtb.diff
)
select dt1, dt2, case when st is null then 0
      else
           case when dt1 <= st then st else dt1 end
    end as st
    , case when ed is null then 0
         else case when ed < dt2 then ed else dt2 end
       end as ed,
    datediff( MINUTE,
    case when st is null then 0
      else
           case when dt1 <= st then st else dt1 end
    end,
    case when ed is null then 0
         else case when ed < dt2 then ed else dt2 end
       end )
     as diff
    
  from jtb
  left
 outer join otb
    on ( otb.ed > jtb.dt1 and otb.st < jtb.dt2 ) 
  

 

 

/###############################################################################/

 

아래가 문제인데...

08시를 기준으로 시작시간, 종료시간 분(시간) 구하기.

우선 급한데로 풀이를 한다면... 아래처럼 구해보겠다. 테스트 까지 1시간 조금 안걸린듯....

 

첫번째 풀이


;with ttb as
(
 select convert( datetime, st ) as st, convert( datetime, ed) as ed    
 from
 (
  values
   ( '2016-05-09 09:00:00', '2016-05-09 09:30:00' ),
    ( '2016-05-09 23:00:00', '2016-05-10 09:00:00' ),
   ( '2016-05-10 22:20:00', '2016-05-11 05:00:00' ) -- 테스트를 위해 추가.
 ) as tb ( st, ed)
/* 문제 :
 이와같은 데이터가 있을때 합산하는 시점은 오전 08시로 기준을 잡고
 2016-05-09 : 570분    (9:00 ~ 09:30 = 30분 + 23:00 ~ 익일 08:00 = 540분)
 2016-05-10 : 60분     (기준시간 08:00~ 09:00 = 60분)
 이렇게 SELECT 를 하고싶은데요.. 너무헷갈립니다 고수분들 조언부탁 드립니다.
*/
)


select dt, sum( case when m > 0 then m else 0 end) as sum_m
from
(
 SELECT  convert( varchar(10), st, 121) as dt,
   case when DATEDIFF( DAY, st, ed ) > 0 and CONVERT( datetime, CONVERT( varchar(10), ed , 112) + ' 08:00:00' ) < ed then DATEDIFF( MINUTE, st, CONVERT( datetime, CONVERT( varchar(10), ed , 112) + ' 08:00:00' ) )
     else DATEDIFF( MINUTE, st, ed)
   end as m
   FROM  ttb

 UNION ALL

 SELECT  convert( varchar(10), ed, 121) as dt,
   DATEDIFF( MINUTE, CONVERT( datetime, CONVERT( varchar(10), ed , 112) + ' 08:00:00' ), ed ) as m
   FROM  ttb
 WHERE DATEDIFF( DAY, st, ed ) > 0 and CONVERT( datetime, CONVERT( varchar(10), ed , 112) + ' 08:00:00' ) < ed
) s
group by dt
 

 

두번재 풀이 ( 10분만에 썼음. )

 

위 식에서 다음날 08시 기준이니까 -8시간 하면 00시 정각이 되니 이것으로 시간을 구함.

 

;with ttb as
(
 select DATEADD( HOUR, -8, convert( datetime, st )) as st, DATEADD( HOUR, -8, convert( datetime, ed)) as ed    
 from
 (
  values
   ( '2016-05-09 09:00:00', '2016-05-09 09:30:00' ),
    ( '2016-05-09 23:00:00', '2016-05-10 09:00:00' )
--   ( '2016-05-10 22:20:00', '2016-05-11 05:00:00' )  -- 테스트를 위해 추가.
 ) as tb ( st, ed)
/* 문제 :
 이와같은 데이터가 있을때 합산하는 시점은 오전 08시로 기준을 잡고
 2016-05-09 : 570분    (9:00 ~ 09:30 = 30분 + 23:00 ~ 익일 08:00 = 540분)
 2016-05-10 : 60분     (기준시간 08:00~ 09:00 = 60분)
 이렇게 SELECT 를 하고싶은데요.. 너무헷갈립니다 고수분들 조언부탁 드립니다.
*/
)

select dt, sum( case when m > 0 then m else 0 end) as sum_m
from
(
 select convert( varchar(10), st, 121) as dt, DATEDIFF( MINUTE, st, ed ) - case when DATEDIFF( DAY, st, ed ) >= 1 then DATEDIFF( MINUTE, CONVERT( datetime, CONVERT( varchar(10), ed , 112) + ' 00:00:00' ), ed ) else 0 end as m
   from ttb
 union all
 select convert( varchar(10), ed, 121) as dt, DATEDIFF( MINUTE, CONVERT( datetime, CONVERT( varchar(10), ed , 112) + ' 00:00:00' ), ed )
   from ttb
  where DATEDIFF( DAY, st, ed ) > 0

) s
group by dt

 

 

 

 

 

 

 

 

  이번에 많은 일을 하는 프로시져 하나가 자꾸 실행중에 죽는다는 얘기를 들었다.

 

음... WCF 패킷이 깨지면서 오류가 뜬다.

ProtocolException ...

그런데 Sql 매니지먼트에서 돌리면 잘 돈다. ( 그러나, 메세지 창에 "경고 : 집계 또는 다른 SET ... " 이란 경고 문구가 엄청 많이 출력되었다. )

 

구글링 겁나 하다보니...

 저런 메세지가 발생할때 윈도우 시스템에 이벤트시스템과 연계되어 성능이 느려질 수 있다고 한다.

 그래서 얘기 했더니 없어지도록 쿼리를 수정하고 성능도 오류도 모두 사라졌다!!!

 

WCF  메세지 패킷 사이즈도 저거때문에 깨지는게 아니었을까???

 

참고.. http://blog.naver.com/PostView.nhn?blogId=nhsang&logNo=40127532573 


https://msdn.microsoft.com/ko-kr/library/system.servicemodel.protocolexception(v=vs.110).aspx
ProtocolException 클래스 : 데이터 전송 프로토콜이 일치하지 않아 원격 상대방과 통신할 수 없는 경우 throw되어 클라이언트에 나타나는 예외입니다.

>> https://social.msdn.microsoft.com/Forums/vstudio/en-US/77097d1f-372a-4c7d-910f-57c9ecd9c5c1/the-maximum-string-content-length-quota-8192-has-been-exceeded-while-reading-xml-data-this-quota?forum=wcf
>> https://social.msdn.microsoft.com/Forums/en-US/f5c3fdeb-65da-4d72-90eb-4c5c31e582d1/nonwcf-service-responds-with-wrong-contentlength-header-protocolexception?forum=wcf

>> WCF 설정에서 예외처리상세여부?
   http://stackoverflow.com/questions/8434206/getting-a-faultexception-when-trying-to-work-with-a-wcf-service

느룡...

 

좀더 빠른 쿼리는 없을까낭...

 

쿼리 속도가  빨라짐!!  장실에서 일보다 생각났음 ㅡ.,ㅡv

테이블 관계를 다시 확인하고, 가공해야 할 최소단위 데이타로 만들어서

58000여건에 대해 쿼리문을 적용하니 40sec -> 2sec 줄었음.

 

결과

 

 

 


;WITH CBS AS (
 SELECT *
 FROM (
 
  VALUES
 
  ('A', '코드A', '', 1),
  ('A.01', '코드A1', 'A', 2),
  ('A.02', '코드A2', 'A', 2),
  ('A.01.01', '코드A01', 'A.01', 3),
  ('A.01.02', '코드A02', 'A.02', 3)
 
 ) AS CBS ( CODE , CODENAME,  UPPER_CODE, CODE_LEVEL)
),
DETAIL AS
(
 SELECT *
 FROM (
  VALUES
 
  ('0001', '코드A.01.01.0001', 'A.01.01',  10),
  ('0002', '코드A.01.01.0002', 'A.01.01',  20),
  ('0003', '코드A.01.02.0003', 'A.01.02',  30),
  ('0004', '코드A.01.02.0004', 'A.01.02',  40),
  ('0005', '코드A.01.02.0005', 'A.01.02',  50)
 ) AS CBS ( CODE , CODENAME,  CBS_CODE, QUANTITY )
)
,
TOTAL AS
(
 SELECT SUBSTRING( CBS_CODE+ '.', 0, CHARINDEX( '.', CBS_CODE + '.')) AS CODE1 ,
   SUBSTRING( cbs_code+ '.', 0, CHARINDEX( '.', cbs_code + '.', CHARINDEX( '.', cbs_code+ '.')+1) ) as code2,
   CBS_CODE AS CODE3,
   CODE AS CODE4,
   CODENAME,
   QUANTITY  
 FROM DETAIL
)
 
SELECT CODE1 , 
  '' CODE_NAME,
  SUM(QUANTITY),
  CODE1 + '.00.00' AS ALIGN
FROM TOTAL t inner join CBS c
on  t.CODE1 = c.CODE and c.CODE_LEVEL = 1
GROUP BY CODE1

UNION ALL 
 
SELECT CODE2,
  '' CODE_NAME,
  SUM(QUANTITY)  ,
  CODE2 + '.00' AS ALIGN
FROM TOTAL t inner join CBS c
on  t.CODE2 = c.CODE and c.CODE_LEVEL = 2
GROUP BY  CODE1, CODE2
 
UNION ALL 
 
SELECT CODE3, 
  '' CODE_NAME,
  SUM(QUANTITY)  ,
  CODE3 AS ALIGN
FROM TOTAL t inner join CBS c
on  t.CODE3 = c.CODE and c.CODE_LEVEL = 3
GROUP BY CODE1, CODE2, CODE3
  
UNION ALL 
 
SELECT CODE4,
  CODENAME,
  SUM(QUANTITY) ,
  CODE3 + CODE4 AS ALIGN 
FROM TOTAL
GROUP BY CODE1,
  CODE2,
  CODE3,
  CODE4,
  CODENAME
 
ORDER BY ALIGN

 

 

sp_helptext 를 하면 애써 맞춰놓은 정렬이 다 깨져 나와서

 

그리드가 아닌 text로 바꿔 실행해서 본다거나 해당 프로시져를 찾아서 수정 눌러서 본다거나...

 

귀찮아진다.

 

기존껀 긴건 짤린다 해서 = varchar( max )

 

sp_helptext를 수정하였음.

 

아래 파란색 글씨만 추가해서 새로 등록함.

 

 1.  스크립트 생성 [ SP_HELPTXT ]

USE [master]
GO
/****** Object:  StoredProcedure [dbo].[SP_HELPTXT]    Script Date: 11/22/2012 11:55:45 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[SP_HELPTXT] 
 @objname nvarchar(776) 
   ,@columnname sysname = NULL 
as 
 
set nocount on 
 
declare @dbname sysname 
,@objid int 
,@BlankSpaceAdded   int 
,@BasePos       int 
,@CurrentPos    int 
,@TextLength    int 
,@LineId        int 
,@AddOnLen      int 
,@LFCR          int --lengths of line feed carriage return 
,@DefinedLength int 
 
/* NOTE: Length of @SyscomText is 4000 to replace the length of 
** text column in syscomments. 
** lengths on @Line, #CommentText Text column and 
** value for @DefinedLength are all 255. These need to all have 
** the same values. 255 was selected in order for the max length 
** display using down level clients 
*/ 
,@SyscomText nvarchar(4000) 
,@Line          nvarchar(255) 
 
select @DefinedLength = 255 
select @BlankSpaceAdded = 0 /*Keeps track of blank spaces at end of lines. Note Len function ignores 
                             trailing blank spaces*/ 
CREATE TABLE #CommentText 
(LineId int 
 ,Text  nvarchar(255) collate database_default) 
 
/* 
**  Make sure the @objname is local to the current database. 
*/ 
select @dbname = parsename(@objname,3) 
if @dbname is null 
 select @dbname = db_name() 
else if @dbname <> db_name() 
        begin 
                raiserror(15250,-1,-1) 
                return (1) 
        end 
 
/* 
**  See if @objname exists. 
*/ 
select @objid = object_id(@objname) 
if (@objid is null) 
        begin 
  raiserror(15009,-1,-1,@objname,@dbname) 
  return (1) 
        end 
 
-- If second parameter was given. 
if ( @columnname is not null) 
    begin 
        -- Check if it is a table 
        if (select count(*) from sys.objects where object_id = @objid and type in ('S ','U ','TF'))=0 
            begin 
                raiserror(15218,-1,-1,@objname) 
                return(1) 
            end 
        -- check if it is a correct column name 
        if ((select 'count'=count(*) from sys.columns where name = @columnname and object_id = @objid) =0) 
            begin 
                raiserror(15645,-1,-1,@columnname) 
                return(1) 
            end 
    if (ColumnProperty(@objid, @columnname, 'IsComputed') = 0) 
  begin 
   raiserror(15646,-1,-1,@columnname) 
   return(1) 
  end 
 
        declare ms_crs_syscom  CURSOR LOCAL 
        FOR select text from syscomments where id = @objid and encrypted = 0 and number = 
                        (select column_id from sys.columns where name = @columnname and object_id = @objid) 
                        order by number,colid 
        FOR READ ONLY 
 
    end 
else if @objid < 0 -- Handle system-objects 
 begin 
  -- Check count of rows with text data 
  if (select count(*) from master.sys.syscomments where id = @objid and text is not null) = 0 
   begin 
    raiserror(15197,-1,-1,@objname) 
    return (1) 
   end 
    
  declare ms_crs_syscom CURSOR LOCAL FOR select text from master.sys.syscomments where id = @objid 
   ORDER BY number, colid FOR READ ONLY 
 end 
else 
    begin 
        /* 
        **  Find out how many lines of text are coming back, 
        **  and return if there are none. 
        */ 
        if (select count(*) from syscomments c, sysobjects o where o.xtype not in ('S', 'U') 
            and o.id = c.id and o.id = @objid) = 0 
                begin 
                        raiserror(15197,-1,-1,@objname) 
                        return (1) 
                end 
 
        if (select count(*) from syscomments where id = @objid and encrypted = 0) = 0 
                begin 
                        raiserror(15471,-1,-1,@objname) 
                        return (0) 
                end 
 
  declare ms_crs_syscom  CURSOR LOCAL 
  FOR select text from syscomments where id = @objid and encrypted = 0 
    ORDER BY number, colid 
  FOR READ ONLY 
 
    end 
 
/* 
**  else get the text. 
*/ 
select @LFCR = 2 
select @LineId = 1 
 
 
OPEN ms_crs_syscom 
 
FETCH NEXT from ms_crs_syscom into @SyscomText 
 
WHILE @@fetch_status >= 0 
begin 
 
    select  @BasePos    = 1 
  select  @CurrentPos = 1 
    select  @TextLength = LEN(@SyscomText) 
 
    WHILE @CurrentPos  != 0 
    begin 
        --Looking for end of line followed by carriage return 
        select @CurrentPos =   CHARINDEX(char(13)+char(10), @SyscomText, @BasePos) 
 
        --If carriage return found 
        IF @CurrentPos != 0 
        begin 
            /*If new value for @Lines length will be > then the 
            **set length then insert current contents of @line 
            **and proceed. 
            */ 
            while (isnull(LEN(@Line),0) + @BlankSpaceAdded + @CurrentPos-@BasePos + @LFCR) > @DefinedLength 
            begin 
                select @AddOnLen = @DefinedLength-(isnull(LEN(@Line),0) + @BlankSpaceAdded) 
                INSERT #CommentText VALUES 
                ( @LineId, 
                  isnull(@Line, N'') + isnull(SUBSTRING(@SyscomText, @BasePos, @AddOnLen), N'')) 
                select @Line = NULL, @LineId = @LineId + 1, 
                       @BasePos = @BasePos + @AddOnLen, @BlankSpaceAdded = 0 
            end 
            select @Line    = isnull(@Line, N'') + isnull(SUBSTRING(@SyscomText, @BasePos, @CurrentPos-@BasePos + @LFCR), N'') 
            select @BasePos = @CurrentPos+2 
            INSERT #CommentText VALUES( @LineId, @Line ) 
            select @LineId = @LineId + 1 
            select @Line = NULL 
        end 
        else 
        --else carriage return not found 
        begin 
            IF @BasePos <= @TextLength 
            begin 
                /*If new value for @Lines length will be > then the 
                **defined length 
                */ 
                while (isnull(LEN(@Line),0) + @BlankSpaceAdded + @TextLength-@BasePos+1 ) > @DefinedLength 
                begin 
                    select @AddOnLen = @DefinedLength - (isnull(LEN(@Line),0) + @BlankSpaceAdded) 
                    INSERT #CommentText VALUES 
                    ( @LineId, 
                      isnull(@Line, N'') + isnull(SUBSTRING(@SyscomText, @BasePos, @AddOnLen), N'')) 
                    select @Line = NULL, @LineId = @LineId + 1, 
                        @BasePos = @BasePos + @AddOnLen, @BlankSpaceAdded = 0 
                end 
                select @Line = isnull(@Line, N'') + isnull(SUBSTRING(@SyscomText, @BasePos, @TextLength-@BasePos+1 ), N'') 
                if LEN(@Line) < @DefinedLength and charindex(' ', @SyscomText, @TextLength+1 ) > 0 
                begin 
                    select @Line = @Line + ' ', @BlankSpaceAdded = 1 
                end 
            end 
        end 
    end 
 
 FETCH NEXT from ms_crs_syscom into @SyscomText 
end 
 
IF @Line is NOT NULL 
    INSERT #CommentText VALUES( @LineId, @Line ) 

declare @lineNo int = 0;
declare @lineCnt int = 0;
declare @printtext varchar(max);
select @lineCnt = COUNT( LineId ) from #CommentText;
while( @lineNo < @lineCnt )
begin
 set @lineNo = @lineNo + 1;
 
 select @printtext = Text from #CommentText
 where LineId = @lineNo ;
 
 print( @printtext );
end
CLOSE  ms_crs_syscom 
DEALLOCATE  ms_crs_syscom 
 
DROP TABLE  #CommentText 
 
return (0) -- sp_helptext 

 

 

 

 

2. 프로시져를 시스템프로시져로 만든다.

USE [master]
GO
exec sp_ms_marksystemobject 'sp_helptxt'

 

3. 단축키에 등록해서 사용하면

 

print 되기때문에 탭유지되면서 잘 나온다 복사 해서 쓰면 됨.

 

 

 

 

오늘 두개의 데이타 베이스의 스키마 비교 쿼리를 만들다 보니..

 

한쪽은 있고 다른 한쪽은 없을 때를 위해 테이블을 복제를 자동화 하려고 했는데...

 

쿼리 한방에 안되네! 

 

  SELECT * INTO 대상DB.dbo.테이블명 FROM 소스DB.dbo.테이블명 where 1 = 2

 

하면 복제는 되는데 키가 복제가 안됨을 확인.

 

 그래서 만들었다. 우선 mssql 스튜디오에서 만들어지는 스크립트와 같이 만들어 보고

 

고정 타입은 아닌것 같은데 군데 군데 하드 코딩이 되있어서 전혀 다르게 동작 가능.

 

음.. ms-sql에서 사용하는 테이블 -> Create 스크립트 쿼리 같은걸 이용하는건 못찾았다.

 

 

declare @tableName nvarchar(775) =QUOTENAME( SCHEMA_NAME() )+'.'+ QUOTENAME(  테이블명  )
declare @Script varchar(max) = ''
declare @FKScript varchar(max) = ''
declare @ctrl varchar(2) = char(13)

declare @type1 varchar(100) = 'char, varchar'
declare @type2 varchar(100) = 'nchar, nvarchar '
declare @type3 varchar(100) = 'numeric,decimal'
 
set @Script = 'CREATE TABLE ' + @tableName + '(' + @ctrl

-- Columns
select @Script += ' '+ QUOTENAME( c.name ) + ' '+ QUOTENAME( TYPE_NAME( system_type_id ) )
  + case
    when CHARINDEX( TYPE_NAME(system_type_id), @type1 , 0) > 0 then '('+ case when c.max_length = -1 then

'max' else CAST( c.max_length as varchar(20) ) end +')'
    when CHARINDEX( TYPE_NAME(system_type_id), @type2 , 0) > 0 then '('+ case when c.max_length = -1 then

'max' else CAST( c.max_length/2 as varchar(20) ) end +')'
    when CHARINDEX( TYPE_NAME(system_type_id), @type3 , 0) > 0 then '('+ CAST( c.precision as varchar(20) )

+ ',' + CAST( c.scale as varchar(20) )+')'
    else '' end
  + ' '+  case when is_nullable = 1 then '' else 'NOT' end + ' NULL,' + @ctrl
from sys.tables t inner join sys.columns c
on  t.object_id = c.object_id
where t.object_id = OBJECT_ID( @tableName )

-- 콤마 제거
set @Script = left( @Script , datalength( @Script )-  datalength( @ctrl ) - datalength( ',') ) + @ctrl

-- Primary Key 생성
select @Script += ' CONSTRAINT ' + QUOTENAME( name ) + ' PRIMARY KEY CLUSTERED '+ @ctrl
from sys.key_constraints
where parent_object_id = object_id( @tableName ) and [TYPE] = 'PK'

select @Script += ' (' + @ctrl

select  @Script += '  '+ QUOTENAME( c.name )+ ' ASC,' + @ctrl
from sys.index_columns i inner join sys.key_constraints k
on  i.object_id = k.parent_object_id
inner join sys.columns c
on  i.object_id = c.object_id and i.column_id = c.column_id
inner join sys.indexes ii
on  i.object_id = ii.object_id and i.index_id = ii.index_id
where parent_object_id = object_id( @tableName ) and k.[TYPE] = 'PK'
 
-- 콤마 제거
set @Script = left( @Script , datalength( @Script )-  datalength( @ctrl ) - datalength( ',') ) + @ctrl
 
select @Script += ' ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,

ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]' + @ctrl
select @Script += ') ON [PRIMARY]' + @ctrl
select @Script += 'GO' + @ctrl

-- Foreign Key 추가
select @FKScript += 'ALTER TABLE '+QUOTENAME( SCHEMA_NAME() )+'.'+ QUOTENAME( OBJECT_NAME( fkeyid )) + ' WITH CHECK ADD 

CONSTRAINT '+ QUOTENAME( OBJECT_NAME( constid ) ) + ' FOREIGN KEY ('+  QUOTENAME( cf.name ) + ')'+ @ctrl
  + 'REFERENCES '+QUOTENAME( SCHEMA_NAME() )+'.'+ QUOTENAME( OBJECT_NAME( rkeyid )) + ' ('+ QUOTENAME(

cr.name )+')'+ @ctrl
  + 'GO' + @ctrl + @ctrl
  + case when parent_object_id is null then 'ALTER TABLE '+QUOTENAME( SCHEMA_NAME() )+'.'+ QUOTENAME(

OBJECT_NAME( fkeyid )) + ' CHECK CONSTRAINT '+ QUOTENAME( OBJECT_NAME( constid ) ) + 'GO' + @ctrl + @ctrl else '' end
from sys.sysforeignkeys f
left
outer join sys.columns cf
on  f.fkeyid = cf.object_id and f.fkey = cf.column_id
left
outer join sys.columns cr
on  f.rkeyid = cr.object_id and f.rkey = cr.column_id

left    -- CHECK KEY < 참조키만 된 컬럼 >
outer join
(
 select  k.parent_object_id, i.column_id
 from sys.index_columns i inner join sys.key_constraints k
 on i.object_id = k.parent_object_id
 and i.object_id = OBJECT_ID( @tableName )     
) t
on  f.fkeyid  = t.parent_object_id  and f.fkey = t.column_id

where fkeyid = OBJECT_ID( @tableName )

 

print @Script
print @FKScript
  
   


이번 프로젝트에서 개량된 도우미 sp시리즈임.
SP 내용은 공개하지 않음!!!( 누구나 만들 수 있는 쿼리이기에... )

 


단축키로 등록해서 사용하면 매우 편리하다.

- SP_JS_OBJECT_EXTENDEDPROPERTY 는 객체의 확장속성등을 정의하는 쿼리와 적용된 내용을 보여준다.


- SP_JS_OBJECT_INFO 는 SP_HELP와 같다 다만 SP를 만들때 사용할 파라미터라든가 설명등을 같이 보여준다.

  기본 정보(객체정보), 컬럼 ( TABLE ), 파라미터( SP, FUNCTION ), RETURN TABLE( FUNCTION ), 객체종속정보

<< FUNCTION 상세보기 >>

<< PROCEDURE 상세보기 >>

<< TABLE 상세보기 >>

- SP_JS_TABLE_SELECT 는 컬럼명을 확장속성으로 병행 표기해준다.


- SP_JS_DEPENDS_HELP 는 제공되는 sp_depends를 수정하여 더 많은 정보들을 보여준다.


- SP_JS_CHANGECOLLATE 은 테이블의 컬럼에 데이타 정렬 속성을 변경할 수 있는 쿼리를 보여준다.

 결과1 : ALTER TABLE ORDERING  ALTER COLUMN CD varchar(20) COLLATE Korean_Wansung_BIN
 결과2 : ALTER TABLE ORDERING  ALTER COLUMN CD varchar(20) COLLATE Korean_Wansung_CI_AS

컬럼이 한두개일땐 모르지만 엄청 많을때 이거 엄청 고된 작업이 된다... 그때 사용하면 된다.

이 SP 들과 연계되는 각종 도우미 프로그램들도 있다...

작업하면서 틈틈히 정리하려고 했던 내용인데
많이는 못하고 일부만 올린다.

#####################################################################################################
## 실행계획 관련해서 찾아본 링크 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
URL : http://rdbms.tistory.com/entry/Order-By-Tuning
DISTINCT : 전체 스캔을 하여 중복된 로우를 제거 한다.
ORDER BY : 전체 스캔을 하여 해당 엑세스가 된 로우를 정렬을 한다.
 
결국 둘다 전체 스캔을 유도 하는 구문입니다.
**  무슨 수를 써던  Order By를 사용하지 않던가 Order By 대상건수를 줄이는 것이 최대 관건이다.
현실적으로 업무상 Order By를 사용하지 않을수는 없을 것이므로  **


/*실행계획 ###############################################################*/
--[Bookmark Lookup] 연산자는 책갈피(행 ID 또는 클러스터링 키)를 사용하여 테이블이나 클러스터형 인덱스에서 해당 행을 조회합니다
--[RID Lookup]은 제공된 RID(행 식별자)를 사용하여 힙을 조회하는 책갈피 조회입니다. 
--   => RID Lookup은 항상 NESTED LOOP JOIN과 함께 사용됩니다. RID Lookup은 물리 연산자입니다.
--      RID Lookup은 Heap Table에서 만 일어난다.(Unique Non-Clustered)
--[Key Lookup] 연산자는 클러스터형 인덱스가 있는 테이블의 책갈피 조회입니다.
--   => Key Lookup은 항상 Nested Loops 연산자와 함께 사용됩니다.
/*[#Tip]
글러스터드 인덱스는 리프레벨에 모든 데이터를 가지고 있기 때문에 같은 컬럼에 넌클러스트를 만드는 경우보다 더 커지게 됩니다 ^^ 룩업이 발생하지 않는다면 같은 컬럼의 클러스터드 보다 넌클러스트 인덱스가 i/o를 좀더 줄일 수도 있습니다  */

↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
#####################################################################################################


/*시간 ###############################################################*/
SELECT Current_Timestamp , GETDATE()

SELECT @@DATEFIRST  -- 7 : 일요일임을 뜻함

-- 현재 WEEKDAY 는 요일, WEEK는 주  곧. '2012-07-02' 는 월요일이고, 27번째주가 된다.
SELECT DATEPART( WEEKDAY, '2012-07-02' ), DATEPART( WEEK, '2012-07-02' )


-- DATEPART ? 로 검색되네?
-- http://msdn.microsoft.com/ko-kr/library/ms174420.aspx
SELECT Current_Timestamp
,  Datename(YYYY, Current_Timestamp ) -- YEAR   : YY, YYYY
,  Datename(MM, Current_Timestamp ) -- MONTH  : MM, M
,  Datename(DD, Current_Timestamp ) -- DAY   : DD, D
,  Datename(HH, Current_Timestamp ) -- HOUR   : HH
,  Datename(N, Current_Timestamp )  -- MINUTE  : MI,N
,  Datename(S, Current_Timestamp )  -- SECOND  : SS,S
,  Datename(MS, Current_Timestamp ) -- MILLISECOND : MS
,  DATEPART(YYYY, Current_Timestamp ) AS [DATEPART]  -- DATEPART로 해봄.

-- ISDATE는 CONVERT 함수와 함께 사용하고 CONVERT 스타일 매개 변수가 지정되고
-- 스타일이 0, 100, 9 또는 109가 아닌 경우에만 결정적입니다.
-- http://msdn.microsoft.com/ko-kr/library/ms187347.aspx
select ISDATE( '20120110130200'), ISDATE( '2012-01-10 13:02:00') -- 시간포멧 확인


--지정된 startdate와 enddate 간에 겹쳐지는 지정된 datepart 범위의 수(부호 있는 정수)를 반환합니다.
--http://msdn.microsoft.com/ko-kr/library/ms189794.aspx
--두 시각의 START - END = RESULT
--DATEPART( YEAR, MONTH, DAY, HOUR, MINUTE, SECOND )값에 따라 체크(분단위까지 유효!)됨.
SELECT DATEDIFF( DAY , '2012-01-11 13:01:00', Current_Timestamp )
/*시간 ###############################################################*/

 

/* 구성함수 ##########################################################*/ 

-- 요청이 들어 오는 고유한 연결의 연결 속성에 대한 정보를 반환합니다
-- http://msdn.microsoft.com/ko-kr/library/bb895240.aspx
select ConnectionProperty('local_net_address') , ConnectionProperty('local_tcp_port')

--현재 사용 중인 언어의 이름을 반환합니다.
select @@Language

--현재 서버에 설정되어 있는 decimal과 numeric 데이터 형식의 전체 자릿수 수준을 반환합니다.
select @@Max_Precision

-- 저장 프로시저가 다른 저장 프로시저를 호출하거나 CLR(공용 언어 런타임) 루틴, 유형
-- 또는 집계를 참조하여 관리 코드를 실행할 때마다 중첩 수준이 증가합니다.
-- 최대값 32를 초과하면 트랜잭션이 종료됩니다.
select @@Nestlevel

-- 서버명
select @@Servername

-- 서버sql버젼
select @@Version

-- 서비스 명
select @@Servicename

-- text 사이즈
select @@Textsize
/* 구성함수  ##########################################################*/ 

 
/*메타데이터 함수  ###################################################*/
Col_Length()
Col_Name()
Columnproperty()
Db_Id()
Db_Name()
Index_Col()
Indexkey_Property()
Indexproperty()
Typeproperty()
Object_Id()
Object_Name()
Objectproperty()
Objectpropertyex()
Sql_Variant_Property()
/*메타데이터 함수  ###################################################*/


 

'# 7) 데이타베이스 > Ms-Sql' 카테고리의 다른 글

MS-SQL] 테이블 스키마 생성 추출 쿼리.  (0) 2012.11.21
MS-SQL] SP_JS_시리즈...  (0) 2012.03.02
MS-SQL ] 1:N 관계에서 n순번 따기...  (0) 2012.03.02
[MS-SQL] 음... 잡다?  (0) 2012.01.02
MS-SQL ] default  (0) 2011.11.18

 이번 프로젝트에서 업체와 관련된 테이블 쿼리에서 업체에 등록된 면허들에 대한 정보를 피벗해서 한번에 보여줘야 하는
쿼리를 처리하면서 잔머리 써본 쿼리 중 짧은 예로 작성해본다.


/*KEY [ tb_1 ( 1 : N ) tb_n ]#################################################################*/
SELECT *
FROM (
 values
  ('a', 'Nm:a')
 , ('b', 'Nm:b')
 , ('c', 'Nm:c')
 , ('d', 'Nm:d')
 , ('e', 'Nm:e')
) as tb_1( K, N )
 
SELECT *
FROM (
 values
  ('a', 'm1:a1')
 , ('a', 'm2:a2')
 , ('b', 'm1:b1')
 , ('e', 'm1:e1')
 , ('e', 'm2:e2')
) as tb_n( K, N )
/*순번 따기 ##################################################################################*/


SELECT
 tb_1.K
, tb_1.N
, ROW_NUMBER() OVER ( ORDER BY tb_1.K ) - RANK() OVER ( ORDER BY tb_1.K ) + 1 [SN_SEQ]
,  tb_n.N
FROM (
 values
  ('a', 'Nm:a')
 , ('b', 'Nm:b')
 , ('c', 'Nm:c')
 , ('d', 'Nm:d')
 , ('e', 'Nm:e')
) as tb_1( K, N )
LEFT OUTER JOIN
(
 values
  ('a', 'm1:a1')
 , ('a', 'm2:a2')
 , ('b', 'm1:b1')
 , ('e', 'm1:e1')
 , ('e', 'm2:e2')
) as tb_n( K, N )
ON tb_1.K = tb_n.K

결과 --- 


보면 a에 대해 1, 2 순번을 따 놓은게 보인다.

이를 이용해 그룹바이를 통해 CASE로 골라내면 옆으로 늘어뜨리는게 가능하다.

다만 CASE로 처리할수 있는 부분이 코드에 따라 달라지므로 이걸 동적으로 처리할 수 있다면 좋겠지만...

우선은 제한적으로 5개, 10개 등으로 제한된 갯수를 표현하는데 무리없는 쿼리라고 보면 된다.

'# 7) 데이타베이스 > Ms-Sql' 카테고리의 다른 글

MS-SQL] SP_JS_시리즈...  (0) 2012.03.02
MS-SQL ] MSDN 일부 정리!  (0) 2012.03.02
[MS-SQL] 음... 잡다?  (0) 2012.01.02
MS-SQL ] default  (0) 2011.11.18
MS-SQL ] 오류 메세지 정리  (0) 2011.11.17


SQL을 다룰때마다 느끼는거지만... 참 재미있는게 많은데..

모르면 손가락 아프게 열심히 쳐야 한다는 사실...

항상 사용하는 것들 또 MSDN을 찾기 구찮아서 정리 해둔다..

아래 내용이면 테이블에 속성이나 자료형 길이등을 뽑아내서 스크립트화 가능한다... 

index_col( object_name( t.object_id ), i.index_id, c.column_id )  인덱스 컬럼인지 확인해준다.

type_name( c.system_type_id )  타입명을 반환

case 
when lower(type_name( c.system_type_id )) in ('char', 'varchar', 'nchar' ,'nvarchar', 'text' )  
then '('+convert( varchar(10), columnproperty( c.object_id, c.name, 'precision' ) )+')'       
when lower(type_name( c.system_type_id )) in ('numeric','decimal' ) 
then '('+ convert(varchar, c.precision) + ',' + convert(varchar, c.scale)+')'    
when lower(type_name( c.system_type_id )) in ('float','datetime', 'smalldatetime', 'tinyint', 'int', 'bigint' ) 
then ''           
else ''           
end  

요건 타입에 사용한 길이를 가져오는것인데...
원래 columnproperty( c.object_id, c.name, 'precision' )  이것인데...
잘 안된다.  총 길이를 자꾸 뱉어내서 정수형 INT, BIGINT같은건 길이가 나타남

 sys.tables  테이블들...

 sys.columns  컬럼들...

 sys.index_columns 인덱스컬럼들...

 sys.extended_properties  확장속성들...

 sys.foreign_key_columns  참조키 컬럼들...


DECLARE @TABLE_NAME NVARCHAR(776) = '테이블명'
 
select  [tablename]  = object_name( c.object_id ),
  [columnname] = c.name,
  [columndesc] = isnull(e.value, ''),
  [pk/fk]   = ltrim( rtrim( case
        when isnull( index_col( object_name( t.object_id ), i.index_id, c.column_id ),'') <> ''
        then 'pk'
        else ''
        end + '' +     
        case
        when isnull( f.constraint_column_id , '' ) <> ''
        then 'fk'
        else ''
        end )),
     [allowIsNull]  = isnull(  case when c.is_nullable = 0 then 'no' end , ''),
  [typename]  = type_name( c.system_type_id ),
  [columnlength] = case
        when lower(type_name( c.system_type_id )) in ('char', 'varchar', 'nchar' ,'nvarchar', 'text' ) 
        then '('+convert( varchar(10), columnproperty( c.object_id, c.name, 'precision' ) )+')'      
        when lower(type_name( c.system_type_id )) in ('numeric','decimal' )
        then '('+ convert(varchar, c.precision) + ',' + convert(varchar, c.scale)+')'   
        when lower(type_name( c.system_type_id )) in ('float','datetime', 'smalldatetime', 'tinyint', 'int', 'bigint' )
        then ''          
        else ''          
        end
from sys.tables t
inner join sys.columns c
on t.object_id = c.object_id -- and t.schema_id = 7
left outer join sys.index_columns i    
on c.object_id = i.object_id and c.column_id = i.column_id 
left outer join sys.extended_properties e         
 on c.object_id = e.major_id and c.column_id = e.minor_id and e.class = 1       
left outer join  sys.foreign_key_columns f     
on c.object_id = f.parent_object_id and c.column_id = f.parent_column_id 
WHERE @TABLE_NAME = case nullif( @TABLE_NAME, '') when '' then '' else OBJECT_NAME( t.object_id ) end  
order by t.name, c.is_nullable, c.name



이 구문이면 테이블정보를 쉽게 파악이 가능하고

확장속성에 스크립트를 걸어두면 속성이 컬럼명 옆에 붙어 나오므로 보기 더 좋음.

이걸 편집하면 테이블 스키마는 쉽게 뽑아낼 수 있음.

참고 # SP_DEPENDS 를 편집에서 나만의 프로시져로 만들어 사용하기도 한다..
각종 시스템 프로시져를 편집에서 사용이 편리한 놈으로 개조해서 사용하기도 함!!!



'# 7) 데이타베이스 > Ms-Sql' 카테고리의 다른 글

MS-SQL ] MSDN 일부 정리!  (0) 2012.03.02
MS-SQL ] 1:N 관계에서 n순번 따기...  (0) 2012.03.02
MS-SQL ] default  (0) 2011.11.18
MS-SQL ] 오류 메세지 정리  (0) 2011.11.17
MS-SQL 2008] Values 의 새로운 발견!!?  (1) 2011.11.11


음 기본값 사용에 있어서... 전에 msdn을 찾아 본 적 있는데...
 
DateTime 도 적용이 되는지?? 확인해보았다.

declare @tb table
(
       dt varchar(20) default Convert(varchar(20), Getdate() , 121 )
)

Insert into @tb values( default );

select * from @tb;


잘 된다!!