【问题标题】:SQL query from Oracle SQL to T-SQL从 Oracle SQL 到 T-SQL 的 SQL 查询
【发布时间】:2019-03-28 11:59:05
【问题描述】:

我有一个用于 Oracle 数据库的子查询,但我想对 SQL Server 数据库使用等效查询。

我不知道如何迁移 TO_TIMESTAMP(TO_CHAR(TO_DATE 部分,也不知道如何在 T-SQL 中使用 rownums 来处理。

甚至可以迁移此查询吗?

SELECT 0 run_id,
      0 tran_id,
      0 sort_id,
      ' ' tran_type,
          10 prod_id,
          72 type_id,
          1 value,
          TO_TIMESTAMP(TO_CHAR(TO_DATE('2016-03-18 00:00:00', 'YYYY.MM.DD HH24:MI:SS') + rownum -1, 'YYYY.MM.DD') || to_char(sw.end_time, 'HH24:MI:SS'), 'YYYY.MM.DD HH24:MI:SS') event_publication,
          EXTRACT (YEAR
                   FROM (TO_DATE('2016-03-18 00:00:00', 'YYYY.MM.DD HH24:MI:SS') + rownum -1)) y,
                  EXTRACT (MONTH
                           FROM (TO_DATE('2016-03-18 00:00:00', 'YYYY.MM.DD HH24:MI:SS') + rownum -1)) mo,
                          EXTRACT (DAY
                                   FROM (TO_DATE('2016-03-18 00:00:00', 'YYYY.MM.DD HH24:MI:SS') + rownum -1)) d,
                                  to_number(to_char (sw.end_time, 'HH24')) h,
                                  to_number(to_char (sw.end_time, 'MI')) mi,
                                  to_number(to_char (sw.end_time, 'SS')) s,
                                  0 ms
FROM all_objects ao,
    settlement_win sw,
    prod_def pd
WHERE pd.prod_id = 10
 AND sw.country = pd.country
 AND sw.commodity = pd.commodity
 AND rownum <= TO_DATE('2016-03-18 23:59:00', 'YYYY.MM.DD HH24:MI:SS') -TO_DATE('2016-03-18 00:00:00', 'YYYY.MM.DD HH24:MI:SS')+1

【问题讨论】:

  • 有可能,是的。 To_DateTo_Char可以翻译成convertrownum可以是row_number() over(order by...)
  • 要取得成功,您需要了解此语句的作用。一旦你知道了,我建议你把它分解成你可以完成的部分。您不应该只是盲目地用 tsql 函数替换 oracle 函数。 from 子句中的那些旧式连接应该是您的第一个改进。 Rownum 需要做一些工作,因为 where 子句中的用法比较晦涩。
  • @ZoharPeled 是的,我也想过为To_DateTo_Char 转换,但不知道to_timestamp 的解决方案
  • @SMor 我对 SQL 有点陌生,但注意到这在某种程度上是过时的代码,我会按照你的想法把它分成几部分

标签: sql sql-server oracle tsql migration


【解决方案1】:

首先要解决的是 rownum 的使用,它在 TSQL 中没有直接等价物,但我们可以模仿它,对于这个特定的查询,您需要认识到表 ALL_OBJECTS 仅用于生成一些行。它对查询没有其他目的。

在 TSQL 中,我们可以使用 CTE 生成行,这有很多变体,但在这里我建议:

;WITH
  cteDigits AS (
      SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL
      SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
      )
, cteTally AS (
      SELECT 
              d1s.digit 
            + d10s.digit * 10
            + d100s.digit * 100  /* add more like this as needed */
         -- + d1000s.digit * 1000  /* add more like this as needed */
            + 1 AS rownum
      FROM cteDigits d1s
      CROSS JOIN cteDigits d10s
      CROSS JOIN cteDigits d100s /* add more like this as needed */
    --CROSS JOIN cteDigits d1000s /* add more like this as needed */
      )

这将按原样快速启动 1000 行,并且可以通过添加更多交叉连接来扩展以生成更多行。请注意,这会返回一个名为 rownum 的列,该列从 1 开始,因此模仿了 Oracle 的 rownum。

所以接下来您可以添加一些剩余的查询,如下所示:

SELECT
      0 run_id
    , 0 tran_id
    , 0 sort_id
    , ' ' tran_type
    , 10 prod_id
    , 72 type_id
    , 1 value
    , convert(varchar, dateadd(day, rownum - 1,'20160318'),121) event_publication

    -- several missing rows here

    , 0 ms
FOM cteTally
INNER JOIN settlement_win sw
INNER JOIN prod_def pd ON sw.country = pd.country AND sw.commodity = pd.commodity
WHERE pd.prod_id = 10
    AND rownum <= datediff(day,'20160318','20160318') + 1

请注意,您实际上不需要 to_timestamp() 等效项,您只需要能够以最大精度的数据输出日期和时间,这似乎是秒级。

要进一步进步(我认为)需要了解sw.end_time 列中保存的数据。如果这可以转换为 mssql datetime 数据类型,那么只需在该值上添加天数即可达到 event_publication 并且类似地,如果 sw.end_time 转换为datetime 数据类型然后使用date_part() 从该列中获取小时、分钟和秒。例如

, DATEADD(day,rownum-1,CONVERT(datetime, sw.end_time)) AS event_publication

另外,如果这样的计算有效,那么可以使用apply operator 来简化整个查询,就像这样

;WITH
  cteDigits AS (
      SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL
      SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
      )
, cteTally AS (
      SELECT 
              d1s.digit 
            + d10s.digit * 10
            + d100s.digit * 100  /* add more like this as needed */
         -- + d1000s.digit * 1000  /* add more like this as needed */
            + 1 AS rownum
      FROM cteDigits d1s
      CROSS JOIN cteDigits d10s
      CROSS JOIN cteDigits d100s /* add more like this as needed */
    --CROSS JOIN cteDigits d1000s /* add more like this as needed */
      )
SELECT
      0 run_id
    , 0 tran_id
    , 0 sort_id
    , ' ' tran_type
    , 10 prod_id
    , 72 type_id
    , 1 value
    , convert(varchar(23), CA.Event_publication, 121) Event_publication
    , datepart(day,CA.Event_publication) dd
    , datepart(month,CA.Event_publication) mm
    , datepart(year,CA.Event_publication) yyyy
    , datepart(hour,CA.Event_publication) hh24
    , datepart(minute,CA.Event_publication) mi
    , datepart(second,CA.Event_publication) ss
    , 0 ms
FOM cteTally
INNER JOIN settlement_win sw
INNER JOIN prod_def pd ON sw.country = pd.country AND sw.commodity = pd.commodity
CROSS APPLY (
      SELECT DATEADD(day,rownum-1,CONVERT(datetime, sw.end_time)) AS event_publication ) CA
WHERE pd.prod_id = 10
    AND rownum <= datediff(day,'20160318','20160318') + 1

注意:IT 可能需要将此 datediff(day,'19000101,'20160318')(等于 42445)包括在 event_date 的计算中,例如

SELECT DATEADD(day,42445 + (rownum-1),CONVERT(datetime, sw.end_time)) AS event_publication

最后一点是,如果您确实需要更高程度的时间精度,但没有明显的要求,您可以使用 datetime2 而不是 datetime

【讨论】:

  • @Mad_Scientist 你能转换你的查询吗?
猜你喜欢
  • 2018-03-28
  • 2017-06-03
  • 2012-10-26
  • 2010-09-07
  • 1970-01-01
  • 2014-11-04
  • 2013-06-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多