怎样写出可在各种数据库间移植的 SQL 语句 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
jingwei8340885
V2EX    分享创造

怎样写出可在各种数据库间移植的 SQL 语句

  •  
  • /a>
      jingwei8340885 2023-12-13 13:25:24 +08:00 1705 次点击
    这是一个创建于 675 天前的主题,其中的信息可能已经有所发展或是发生改变。

    任意的 SQL 语句都可移植是做不到的。因为各种数据库的功能并不一样,某些数据库的语法在另一种数据库中根本就没有对应的实现机制,这时当然就不可能移植了。

    如果我们不用数据库的特殊功能,只在国际标准的范围内,也就是只使用所有数据库的功能交集,其实也相当丰富了,这总该能移植了吧。

    基本的情况确实是可以,比如

    select * from employee where age>50 select area,sum(amount) from orders group by area 

    在所有数据库中都能正常执行。

    但是,还有很多 SQL 函数在各种数据库中的写法不一样,特别是字符串和日期相关的,比如

    MySQL: year( x ) Oracle: extract( year from x ) 

    国际标准没有对这些函数做规定,这种 SQL 语句就会和数据库相关而无法移植了,而这些函数在应用开发中还特别常见。

    Hibernate 能将包括这些函数的标准 HQL 语句转换成不同数据库的 SQL ,但 HQL 支持的函数太少,碰到不认识的函数还是要分别注册,这就丧失了可移植性;而且 HQL 能生成的 SQL 本身也比较简单,覆盖面太窄了。

    转换 SQL 语句中的不同的函数,esProc SPL 是个更好的解决方案。

    esProc SPL 是个 Java 写的开源软件,在这里 https://github.com/SPLWare/esProc

    SPL 中有个 sqltranslate 函数,能够使用了某些“标准”的函数语法书写的 SQL 翻译成各种数据库的函数。比如:

    sql = "select * from Orders where year(OrderDate)=2000" sql.sqltranslate("MySQL") -> "select * from Orders where year(OrderDate)=2000" sql.sqltranslate("Oracle") -> "select * from Orders where extract( year from OrderDate )=2000" 

    sqltranslate 中已经预定义了很多数据库中很多函数的写法,比如:

    sql = "select * from Orders where ADDDAYS(OrderDate,3)>ShipDate" sql.sqltranslate("MySQL") -> "select * from Orders where (OrderDate + INTERVAL 3 DAY)>ShipDate" sql.sqltranslate("Oracle") -> "select * from Orders where (OrderDate + NUMTODSINTERVAL(3, "DAY")>ShipDate" sql.sqltranslate("DB2") -> "select * from Orders where (OrderDate + 3 DAYS )>ShipDate" 

    如果碰到了新的函数或新的数据库,esProc 还允许程序员自己在配置表中增加。

    esProc 提供了 JDBC 接口,很容易被集成进 Java 应用以实现 SQL 移植。这里有完整的使用方法:SPL:跨数据库移植 SQL

    esProc 当然并不是仅仅为了移植 SQL 而设计的,SPL 是功能强大的结构数据处理语言,转换 SQL 只是 SPL 顺便实现的一点点小功能而已,SPL 更多的作用在于替代 SQL 实现复杂的查询运算。

    当计算需求复杂时我们会用到某些数据库特有的语法或函数。比如 Oracle 有个 KEEP 函数可以方便地计算每组的第一条/最后一条记录,其它很多数据库都没有。如果 SQL 语句中用到了这个函数,就不能再用上面的简单办法移植到其它数据库上了。这种情况下,我们可以只用基本的 SQL 读取数据,而把更复杂的运算交给 SPL 来做,SPL 是完全不依赖于数据库的,这样写出来的代码就可以继续拥有可移植性了。

    select Area, max(Amount) KEEP( dens_rank first order by extract( month from OrderDate) ) from Orders where extract( year from OrderDate)=2000 group by Area 

    这条 Oracle 的 SQL 语句可以用 SPL 改成这样:

    A B
    1 'select OrderDate, Area, Amount from Orders where year(OrderDate)=2000 /读数 SQL
    2 =A1.sqltranslate("Oracle") /转成目标数据库的 SQL
    3 =db.query@x(A2) /读出数据
    4 =A3.group(Area;~.minp@a(month(OrderDate)).max(Amount)) /用 SPL 计算目标结果

    ( SPL 代码写在格子里,这和普通程序语言很不像,参考这里:写在格子里的程序语言

    SPL 拥有所有 SQL 的运算能力(过滤、分组、连接等),并且都是自行实现的(不是翻译成 SQL ),不依赖于任何数据库,保证这个代码可以在任何数据库上正确执行,就可以移植了。

    事实上,SPL 提供的运算能力远比 SQL 更强大丰富(所以也不可能翻译成 SQL 执行)。有些即使可以用 SQL 写出来的复杂运算,用 SPL 也会更简单。比如我们经常举例的任务:计算一支股票最长连续上涨的天数,SQL 要写成多层嵌套,冗长且难懂:

     select max(ContinuousDays) from ( select count(*) ContinuousDays from ( select sum(UpDownTag) over (order by TradeDate) NoRisingDays from ( select TradeDate,case when Price>lag(price) over ( order by TradeDate)then 0 else 1 end UpDownTag from Stock )) group by NoRisingDays ) SPL 提供了更多的基础函数,同样的计算逻辑,写起来要简单得多: Stock.sort(TradeDate).group@i(Price<Price[-1]).max(~.len()) 

    这样的代码,是不是既开发简单又容易移植了?

    SPL 资料

    1 条回复    2023-12-14 10:00:01 +08:00
    beetlerx
        1
    beetlerx  
       2023-12-14 10:00:01 +08:00
    orm 不就是用来干这事的
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2505 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 19ms UTC 10:49 a href="/worldclock#pvg">PVG 18:49 LAX 03:49 JFK 06:49
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86