2013年10月29日 星期二

取得具有相同數字之數值(2)

本範例將查詢具有相同數字之數值(如:11122888等),此範例將利用2組數值數列產生111111222….之數列,以 Table Join方式進行篩選,即可將符合相同數字之數值保留。SQL及結果如下:

SQL
MSSQL
WITH Filter
AS
(
--產生, 11, 111, 1111...數列
SELECT N.number
       , REPLICATE(CAST(N.number AS VARCHAR), D.number) Val_C
       , CAST(REPLICATE(CAST(N.number AS VARCHAR), D.number) AS BIGINT) Val
FROM master.dbo.spt_values N  -- 產生1~9數值數列
, master.dbo.spt_values D  -- 產生1~D(位數)數列
, (--重點.以取得最大數值,以為底取LOG即可得最大數值之位數(ex: 222為位數)
        SELECT CEILING(LOG10(MAX(Val))) Digits   -- 3. LOG值進位
                , LOG10(MAX(Val)) "Digits(LOG10)"   -- 2.最大數值以10為底取LOG
              , MAX(Val)          MaxVal           -- 1.取得最大數值
          FROM #Data
        ) U
WHERE 1=1
       AND N.name IS NULL
       AND D.name IS NULL
       AND N.number BETWEEN 1 AND 9        -- 產生~9數值數列
       AND D.number BETWEEN 1 AND U.Digits -- 產生~D(位數)數列
--ORDER BY N.number, D.number
)
------------
SELECT D.*
FROM #Data D, Filter F
WHERE D.Val = F.Val

: 重點在於虛線位置,以產生111111222….
ORACLE
WITH Filter
AS
(
--產生, 11, 111, 1111...數列
SELECT N.NUM
       , D.Digits
       , RPAD(N.NUM , D.Digits , N.NUM) VAL
FROM
    (
    SELECT LEVEL NUM
    FROM DUAL
    CONNECT BY LEVEL <=9
     ) N,
     (
    SELECT LEVEL Digits
    FROM DUAL,
         (
         SELECT CEIL(LOG(10, MAX(Val))) Digits
             , MAX(Val) MaxVal
         FROM TEST
         )
    CONNECT BY LEVEL <= Digits
     ) D
)
SELECT D.*
FROM TEST D, Filter F
WHERE D.Val = F.Val

執行結果如下:
             











SQL /說明
說明
MSSSQL
--產生, 11, 111, 1111...數列
SELECT N.number
,REPLICATE(CAST(N.number AS VARCHAR), D.number) Val_C
,CAST(REPLICATE(CAST(N.number AS VARCHAR), D.number) AS INT) Val
FROM master.dbo.spt_values N  -- 產生1~9數值數列
, master.dbo.spt_values D  -- 產生1~D(位數)數列
, (--重點.以取得最大數值,以為底取LOG即可得最大數值之位數(ex: 222為位數)
 SELECT CEILING(LOG10(MAX(Val))) Digits -- 3. LOG值進位
    , LOG10(MAX(Val)) "Digits(LOG10)"   -- 2.最大數值以10為底取LOG
       , MAX(Val)          MaxVal            -- 1.取得最大數值
    FROM #Data
    ) U
WHERE 1=1
       AND N.name IS NULL
       AND D.name IS NULL
       AND N.number BETWEEN 1 AND 9        -- 產生~9數值數列
       AND D.number BETWEEN 1 AND U.Digits -- 產生~D(位數)數列
--ORDER BY N.number, D.number
ü   利用2組數值數列產生111111222…
ü   1組數列為1~9;第2組為1~{最大位數}
ü   最大位數是以
- 取得最大數值
- 最大數值取LOG10
- 再以CEILING函數無條件進位,即為最大位數。
ü   REPLICATE函數為將字串值重複指定的次數。以產生重複數字數值字串。
ü   數值字串轉型為數字。

ORACLE
--產生, 11, 111, 1111...數列
SELECT N.NUM
       , D.Digits
       , RPAD(N.NUM , D.Digits , N.NUM) VAL
FROM
    (
    SELECT LEVEL NUM
    FROM DUAL
    CONNECT BY LEVEL <=9
     ) N,
     (
    SELECT LEVEL Digits
    FROM DUAL,
         (
         SELECT CEIL(LOG(10, MAX(Val))) Digits
             , MAX(Val) MaxVal
         FROM TEST
         )
    CONNECT BY LEVEL <= Digits
     ) D
ü   ORACLEREPLICATE函數,可使用RPAD函數替代。
ü   其餘概念與MSSQL同,略。
此方法虛線部分執行結果如下(9*3 =27筆):



2013年10月26日 星期六

取得具有相同數字之數值

本範例將查詢具有相同數字之數值(如:11122888等),主要撰寫概念是以每個數值第一個數字(即最大位數)為除數,除以整個數值,若商數均為數字1、如111111…等,即代表此數值每個數字相同。MSSQLORACLE均應用相同概念撰寫,SQL及結果如下:

SQL
MSSQL
SELECT D.VAL
 , N.Num
 , D.VAL / N.Num Quotient --重點2.{數值}除以{數值}1碼之{數字}得到商數,均為1者即為所求
 , PATINDEX('%[^1]%',CAST((D.VAL/N.Num) AS varchar)) Pos --重點3.商數第1個數字不為1之位置
FROM
(
 SELECT Val
 FROM (VALUES (1), (2), (3), (11), (121), (21), (22), (36), (116), (222)
          ) AS Data(Val)
) D,
     (--產生{1~9}數值數列,取自master.dbo.spt_values
      SELECT CAST(number AS VARCHAR)  Num
      FROM master.dbo.spt_values
      WHERE name IS NULL
           AND number BETWEEN 1 AND 9
     ) N
WHERE 1=1
AND D.VAL LIKE N.Num + '%' --重點1.{數值第1}{數字1~9}進行Table Join
--AND PATINDEX('%[^1]%', CAST((D.VAL / N.Num) AS VARCHAR)) = 0
ORDER BY N.NUM, D.VAL
: VAULES2008功能,先前文章以介紹,請自行參考不贅述。

ORACLE
SELECT D.Val
 , N.Num
 , TRUNC(D.VAL/N.Num) Quotient --重點2.{數值}除以{數值}1碼之{數字}得到商數,均為1者即為所求
 , REGEXP_INSTR(TRUNC(D.VAL/N.Num), '[^1]') Pos --重點3.商數第1個數字不為1之位置
FROM
(
  SELECT Column_Value AS Val
  FROM TABLE(Split_tbl('1','2','3','11','121','21','22','36','121','222'))
) D,
  (--產生{1~9}數值數列,CONNECT BY產生
  SELECT LEVEL Num
  FROM DUAL
  CONNECT BY LEVEL <=9
  ) N
WHERE 1=1
      AND D.VAL LIKE N.Num ||'%' --重點1.{數值第1}{數字1~9}進行Table Join
      --AND REGEXP_INSTR(TRUNC(D.VAL/N.Num), '[^1]') = 0
ORDER BY N.NUM, D.VAL

: Split_tbl為自訂type配合table函數,可將CSV字串轉為Table,後續文章再行介紹。
:虛線段落如無法瞭解,可自行CREATE TABLE / INSERT TABLE建立測試資料。

重點1: Join方式得到數值第一個數字。
重點2: 將數值除以第一個數字,若商數之數字均為1,則代表其數字均相同。商數取得方法『數值函數』之說明。
重點3: Regular Expression找出商數中第一個非為1之位置,如均為1則回傳為0MSSQL可應用PATINDEX函數;ORACLE則可使用REGEXP_INSTR,可參閱拙著或後續文章將說明。


執行結果
說明

欄位說明
ü   VAL: 數值
ü   NUM: 數值第1碼數字
ü   Quotient: VAL除以NUM之商數
ü   Pos:不為1之數字位置,0代表均為1

 Pos0者,即為所求,實際應用時,將WHERE條件式中註解該行啟用即可。執行結果如下: