728x90

검색 조건에 특정 기간(datetime)을 주어 조회할 경우 잠깐! 생각해 보자~!


예를 들어 2007-01-01 ~ 2007-01-10의 기간동안  가입한 회원 목록을 조회해야 한다면~
BETWEEN이나.. >= <= 를 이용하여... [ EX1 ]과 같은 방법으로 많이 한다~!


간단히 생각하믄... 맞는결과가 나올듯 하지만... 그렇지 않다.
검색필드가  datetime형식이므로  [년-월-일]은 [년-월-일 00시 00분 000초]인 셈이다.
그래서 [ EX1 ]의 방법을 이용할 경우 2007-01-10의 데이터도 조회되길 원하였으나 결과는 그렇지 못할 것이다.


그래서..[ EX2 ]처럼... 조회하였다...  훔... 허접해. 보인닷!~~~ 그러나 결과는.. 굿~!


그럼... [EX3]은??  마지막날에 +1일을 더하여 마지막날의 데이터도 포함할 수 있도록 하는것이다~..
그중에.. 이게 젤루 맘에 든다...
물론.. 2007-01-11 00:00:000 인 데이터가 존재한다면... 이건 에러다 -_-;; 
하루를 더한것에서 -1 millisecond를 빼? ㅋㅋ (생략하겠음~ -_-;;)


마지막으로.. [EX4]는 필드값을 [YYYY-MM-DD]형식으로 변형하여 조회할 수도 있겠다~

그러나 값을 변형하여.. 보여주는것도 그렇고.. 인덱스도 안걸어 놓겠지만.. 만약 있다면 인덱스 걸리지도 않을꺼구... 맘에.. 안든다..


암튼 방법이야 많겠지만... [ EX1 ]처럼... 틀린 검색 조건을 주어서는 안된다는걸.. 말하고 싶을 뿐이닷~


-- [ EX1 ]
SELECT *
FROM dbo.MEMBER
WHERE 등록일시 BETWEEN '2007-01-01' AND '2007-01-10'


-- [ EX2 ]
SELECT *
FROM dbo.MEMBER
WHERE 등록일시 BETWEEN '2007-01-01' AND '2007-01-10 59:59:999'


-- [ EX3]
SELECT *
FROM dbo.MEMBER
WHERE 등록일시 BETWEEN '2007-01-01' AND DATEADD(DAY, 1, '2007-01-10')


-- [ EX4]
SELECT *
FROM dbo.MEMBER
WHERE CONVERT(VARCHAR(10), 등록일시, 121) BETWEEN '2007-01-01' AND '2007-01-10'

728x90

와일드카드 문자 설명
% 0개 이상의 문자를 가진 문자열 WHERE title LIKE '%computer%'는 책 제목에 'computer' 단어가 있는 모든 책 제목을 찾습니다.
_ (밑줄) 단일 문자 WHERE au_fname LIKE '_ean'은 ean으로 끝나는 모든 4문자 이름을 찾습니다(Dean, Sean 등).
[ ] 지정된 범위([a-f]) 또는 집합([abcdef])에 있는 단일 문자 WHERE au_lname LIKE '[C-P]arsen'은 arsen으로 끝나고 C와 P 사이의 단일 문자로 시작하는 저자의 성을 찾습니다. 예를 들면 Carsen, Larsen, Karsen 등입니다.
[^] 지정된 범위([^a-f]) 또는 집합([^abcdef])에 없는 단일 문자 WHERE au_lname LIKE 'de[^l]%'은 de로 시작하고 이어지는 문자가 l이 아닌 저자의 성을 모두 찾습니다.


기호 의미
LIKE '5[%]' 5%
LIKE '[_]n' _n
LIKE '[a-cdf]' a, b, c, d 또는 f
LIKE '[-acdf]' -, a, c, d 또는 f
LIKE '[ [ ]' [
LIKE ']' ]
LIKE 'abc[_]d%' abc_d 및 abc_de
LIKE 'abc[def]' abcd, abce 및 abcf

-- MSSQL 2005의 AdventureWorks 를 사용

USE AdventureWorks;
GO

-- dm으로 시작하는 Name 모두 검색

SELECT Name
FROM sys.system_views
WHERE Name LIKE 'dm%'

-- []와일드 카드 문자를 사용하여 여러 조건을 만족하는 데이타 검색

-- LastName이 A, J로 시작하는 데이타 검색

SELECT ContactID, FirstName, LastName
FROM Person.Contact
WHERE LastName LIKE '[AJ]%';


ESCAPE절 사용하여 와일드카드 문자를 포함하는 데이터 조회하기

데이타거 100% 라는 값 자체로 입력되어 있을때 '100%'가 들어 있는 데이터를 검색하고싶을때.


USE tempdb;
GO


IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
      WHERE TABLE_NAME = 'mytbl2')
   DROP TABLE mytbl2;
GO
USE tempdb;
GO

CREATE TABLE mytbl2
(
 c1 sysname
);
GO
INSERT mytbl2 VALUES ('Discount is 10-15% off');
INSERT mytbl2 VALUES ('Discount is .10-.15 off');
GO

SELECT c1
FROM mytbl2
WHERE c1 LIKE '%10-15!% off%' ESCAPE '!';
GO

참고 : http://msdn2.microsoft.com/ko-kr/library/ms179859.aspx

728x90

MS-SQL 2005에서는 순위를 쉽게 구할수 있도록 몇개의 함수를 추가로 제공한다.

이미 Oracle에서는 제공되는 함수들 이므로 간략하게 비교해 볼까 한다~

DB는 ADVENTUREWORKS의 테이블을 이용할 것이다.


USE ADVENTUREWORKS
GO

를 꼭 하자~


구문

<FUNCTION_NAME>() OVER(

      [PARTITION BY <PARTITION_BY_LIST>] 

      ORDER BY <ORDER_BY_LIST> )


EX) ROW_NUMBER() OVER( PARTITION BY COL3 ORDER BY COL1 )


1. OVER ( ORDER BY <ORDER_BY_LIST> )는 순위를 결정하는 정렬 조건으로 필수이다.

2. PARTITION BY를 지정하지 않으면 순위값은 모든 테이블 행을 대상으로 순위가 계산되지만

지정하면 행 그룹내에서 개별적으로 계산될 수 있다.


☞ 순위 함수


◑ ROW_NUMBER

결과행에 순차적으로 값을 지정할 수 있다. (페이징할때 유용하다.. 이젠 고생할 필요없다 ㅋㅋㅋ)

1. PARTITION BY를 사용할때와 하지 않을때의 차이 비교

SELECT
     CONVERT(VARCHAR(10), ORDERDATE, 121) AS ORDERDATE,
     ROW_NUMBER()OVER (ORDER BY ORDERDATE DESC) AS ROWNUM,
     ROW_NUMBER()OVER (PARTITION BY CONVERT(VARCHAR(10), ORDERDATE, 121) ORDER BY ORDERDATE DESC) AS SUBROWNUM,
     CUSTOMERID,
     TOTALDUE
FROM SALES.SALESORDERHEADER
WHERE TOTALDUE > 100
ORDER BY ROWNUM, SUBROWNUM

사용자 삽입 이미지




















빨간 박스의 ROWNUM과 SUBROWNUM을 비교하여 보자.

SUBROWNUM은 ORDERDATE내에서 다시 1부터 순차적으로 값이 지정되는걸 확인 할 수 있다.


2. 페이징에 적용하여 볼까?

DECLARE @intPageSize INT -- 보여줄갯수
DECLARE @intCurrentPage INT -- 보여줄 페이지 번호
DECLARE @intIDFrom INT
DECLARE @intIDTo INT
 
SET @intPageSize = 10
SET @intCurrentPage = 2
SET @intIDFrom = (@intPageSize * @intCurrentPage) + 1
SET @intIDTo = ( @intIDFrom +  @intPageSize ) -1

SELECT ROWNUM, NAME
FROM ( SELECT ROW_NUMBER()OVER(ORDER BY VENDORID) AS ROWNUM, VENDORID, NAME
  FROM Purchasing.Vendor ) AS D
WHERE ROWNUM BETWEEN @intIDFrom AND @intIDTo
ORDER BY VENDORID
 
페이징에 대해선 이전에 썼던 포스트를 참조 하자!
 
3. 오라클의 ROW_NUMBER()도 똑같다.
SELECT DEPTNO, ROW_NUMBER() OVER(PARTITION BY DEPTNO ORDER BY EMPNO) RID, EMPNO FROM EMP

 

4. 오라클에서 ROWNUM를 이용하여 페이징 기능 구현

SELECT *
FROM (
      SELECT ROWNUM AS INX, T.* FROM (
             SELECT EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO
            FROM EMP
            ORDER BY EMPNO ) T
) T
WHERE INX BETWEEN 1 AND 10;
ROWNUM은 ORDER BY EMPNO가 실행되기 전에 순위값이 지정되므로 정렬 후 ROWNUM을 지정하여야 한다.

◑ RANK 와 DENSE_RANK

순차적으로 값을 제공하는점에서는 ROW_NUMBER과 유사하지만 동률이 적용된다는점에서는 다르다.

1. ROW_NUMBER, RANK, DENSE_RANK 비교

SELECT
    CONVERT(VARCHAR(10), ORDERDATE, 121) AS ORDERDATE,
    ROW_NUMBER()OVER (ORDER BY TOTALDUE DESC) AS ROWNUM,
    RANK() OVER (ORDER BY TOTALDUE DESC)AS RANK,

    DENSE_RANK()OVER (ORDER BY TOTALDUE DESC) AS DENSE_RANK,
    DENSE_RANK()OVER (PARTITION BY CONVERT(VARCHAR(10), ORDERDATE, 121) ORDER BY TOTALDUE DESC) AS SUB_DENSE_RANK,

    TOTALDUE
FROM SALES.SALESORDERHEADER
WHERE TOTALDUE > 100
ORDER BY DENSE_RANK, SUB_DENSE_RANK

사용자 삽입 이미지

결과의 1694와 1695 ROW는 TOTALDUE가 같으므로 동률이다.

ROWNUM은 무조건 순차적으로 값을 제공하지만 RANK와 DENSE_RANK는 동률이 적용된다.

1696 ROW를 보면 RANK는 1694순위가 2개였으므로 다음순위는 1696순위가 되지만

DENSE_RANK는 1694순위가 2개였더라도 다음 순위는 1695가 된다.

PARTITION BY는 날짜별로 순위를 제공한다.


2. 오라클에서도 똑같다

SELECT
    MGR,
    ROW_NUMBER() OVER(ORDER BY SAL DESC) AS INX,
    RANK() OVER(ORDER BY SAL DESC) AS RANK,
    DENSE_RANK()OVER(ORDER BY SAL DESC) AS DENSE_RANK,
    DENSE_RANK()OVER(PARTITION BY MGR ORDER BY SAL DESC) AS SUB_DENSE_RANK,
    SAL
FROM EMP


◑ NTILE

데이터를 지정된 그룹수로 분리할 수 있다.

SELECT
    CONVERT(VARCHAR(10), ORDERDATE, 121) AS ORDERDATE,    
    NTILE(20) OVER(ORDER BY ORDERDATE) AS GRPNO,
    NTILE(5) OVER(PARTITION BY CONVERT(VARCHAR(10), ORDERDATE, 121) ORDER BY ORDERDATE) AS SUB_GRPNO,
    TOTALDUE
FROM SALES.SALESORDERHEADER
WHERE MODIFIEDDATE > CONVERT(DATETIME, '2004-08-01', 121)
ORDER BY ORDERDATE, GRPNO, SUB_GRPNO

사용자 삽입 이미지

전체 187건 ORDERDATE가 2004-07-26인것은 32건 이다.
187/20 = 9.35
전체 20그룹을 각 9건씩 채우면 180건이고 7건이 남는다. (20 * 9 = 180)
남은 7건은 1그룹부터 순차적으로 채워진다.
1~7그룹 : 10건 (7*10 = 70)
8~20그룹 : 9건 (13 * 9 = 117)
PARTITION BY 부분도 위에서 설명한 내용과 같다.~
 
2. 이것역쉬 오라클에서도 같네?
SELECT 
       EMPNO,
       MGR,
       NTILE(5) OVER(ORDER BY EMPNO) AS GRPNO,
       NTILE(2) OVER(PARTITION BY MGR ORDER BY EMPNO) AS SUB_GRPNO
FROM EMP
ORDER BY GRPNO, SUB_GRPNO

+ Recent posts