【问题标题】:How to emulate LPAD/RPAD with SQLite如何使用 SQLite 模拟 LPAD/RPAD
【发布时间】:2011-09-28 09:55:57
【问题描述】:

我很好奇如何以最一般的方式正式模拟 SQLite 的 RPAD 和 LPAD 函数。目标是能够做到

LPAD(column, character, repeat)
RPAD(column, character, repeat)

对于非常量表列columncharacterrepeat。如果characterrepeat 是已知常量,那么这将是一个很好的可行解决方案:

但是如果上面应该这样执行呢:

SELECT LPAD(t.column, t.character, t.repeat) FROM t
SELECT LPAD(t.column, some_function(), some_other_function()) FROM t
SELECT LPAD(t.column, :some_bind_variable, :some_other_bind_variable) FROM t

这个LPAD 函数一般如何被模拟?我迷失了各种可能性:

一个相关问题:

【问题讨论】:

  • 你用什么来连接 SQLite?数据库引擎有一个“插件”架构,您可以在其中定义附加函数 (UDF) 并将它们包含在您的 sql...
  • @Stobor:好点。我正在与一个非官方的JDBC driver 联系。这一切都将在jOOQ(Java 中的 SQL 抽象层)中实现。所以不幸的是,我不能依赖可能的 UDF,只能依赖 SQLite 核心中提供的东西
  • 您是为 jOOQ 开发,还是使用 jOOQ api?无论哪种方式,我很确定您可以使用 java UDF...
  • @Stobor:我是 jOOQ 的创建者。因此,我不希望创建对这个特定 JDBC 驱动程序的依赖。但是对于可能正在使用 jOOQ API 的人来说,您的解决方案仍然相当不错。如果可以使用 SQLite 的编译指示发现这些 UDF,那么 jOOQ 应该原生支持它们!
  • 没有问题!您的问题主要只是促使我自己弄清楚如何做到这一点。 (我以前在 Python 中做过,所以我怀疑它应该是可能的。)jOOQ 看起来很有趣,祝你好运。

标签: sql sqlite emulation padding


【解决方案1】:

可能是这样的:

  • LPAD(@orig_text, @padding_char, @padding_length):

    SELECT
      SUBSTR(
        REPLACE(
          CAST(ZEROBLOB(@padding_length) AS TEXT),
          CAST(ZEROBLOB(1) AS TEXT),
          @padding_char
        ) + @orig_text,
        -@padding_length,
        @paadding_length
      )
    
  • RPAD(@orig_text, @padding_char, @padding_length):

    SELECT
      SUBSTR(
        @orig_text + REPLACE(
          CAST(ZEROBLOB(@padding_length) AS TEXT),
          CAST(ZEROBLOB(1) AS TEXT),
          @padding_char
        ),
        1,
        @padding_length
      )
    

【讨论】:

  • 这是个好主意,但似乎行不通。看起来将 zeroblob(N) 值转换为 TEXT 会导致“空”字符串。在C 中,字符串以0x00 字符结尾,这就是zeroblob 中包含的内容:-(
  • @Lukas Eder:真可惜。这是迄今为止我能找到的唯一可参数化的东西。很抱歉让您失望了。
  • 别担心,你没有让我失望 :) 这实际上是一个非常有创意的解决方案。我只是一次又一次地对 SQLite 缺乏功能感到惊讶。他们怎么会有一个无用的randomblob() 函数,却没有填充函数……?哦,好吧
【解决方案2】:

这对你来说更讨厌:

X = padToLength
Y = padString
Z = expression

RPAD(对于 LPAD,Z 被连接在后面):

select 
    Z || 
    substr(
        replace(
            replace(
                substr(
                    quote(zeroblob(((X - length(Z) - 1 + length(Y)) / length(Y) + 1) / 2)), 
                    3
                ), 
                "'", 
                ""
            ), 
            "0", 
            Y
        ),
        1,
        (X - length(Z))
    )

例子:

sqlite> select "foo" || replace(replace(substr(quote(zeroblob((2 + 1) / 2)), 3, (2 - length("foo"))), "'", ""), "0", "W");
foo
sqlite> select "foo" || replace(replace(substr(quote(zeroblob((7 + 1) / 2)), 3, (7 - length("foo"))), "'", ""), "0", "W");
fooWWWW

Sqlite 本来就是非常轻量级的,所以我不得不有点不同意你对缺乏功能感到“惊讶”的评论。但是,我同意应该有一种更简单的方法来进行填充,如果只是因为存在 trim 函数。

【讨论】:

  • 哈,看起来很疯狂!我稍后会检查这是否有效:-) 我知道轻量级。但是LPAD/RPAD可以用10行C代码编码。另一方面,我仍然没有看到randomblob() 的用例...
  • 你可能想通过 sqlite-users@sqlite.org 进行游说 ;-)
  • 这真的有效! quote() 发挥了作用,尽管当不得不再次删除 X'...' 的东西时,它并没有使事情更具可读性...... :) 如果没有人提出任何更短的解决方案,我将奖励赏金(我怀疑) .无论如何,您的解决方案将使其成为jOOQ,这样未来的 SQLite 用户将不必考虑再次模拟LPADRPAD
  • @LukasEder:太酷了; jOOQ 是 .Net 中的 EF 的等价物吗?
  • 确实 SQL 并没有区分字符和字符串;但可以肯定的是,它可能会更糟糕。
【解决方案3】:

一种 JDBC/自定义函数方法(可能不适合您的具体情况,但可能能够适应)。使用来自 SqliteJDBC Custom Functions 以及来自 Apache commons.lang.StringUtils 的 rightPadleftPad 函数的灵感:

import java.sql.*;
import org.sqlite.Function;

public class Test 
{
  public static void main(String[] args) 
  {
    Connection conn = getConnection();

    conn.createStatement().execute("SELECT LPAD(t.column, t.character, t.repeat) FROM t");
    conn.createStatement().execute("SELECT RPAD(t.column, t.character, t.repeat) FROM t");

    conn.close();
  }

  public static Connection getConnection() 
  {
    Class.forName("org.sqlite.JDBC");
    Connection conn = DriverManager.getConnection("jdbc:sqlite:");

    /* Left Padding UDF  */
    Function.create(conn, "LPAD", new Function() 
      {
        protected void xFunc() throws SQLException 
        {
            String text = value_text(0);
            /* uses first character of supplied padding */
            char paddingCharacter = value_text(1).charAt(0);
            int repeat = value_int(2);

            int padLength = repeat - text.length();
            if(padLength <= 0)
            {
               result(text);
            }

            char[] padding = new char[padLength];
            Array.fill(padding, paddingCharacter);
            result(new String(padding).append(text));
        }
    });

    /* Right Padding UDF  */
    Function.create(conn, "RPAD", new Function() 
      {
        protected void xFunc() throws SQLException 
        {
            String text = value_text(0);
            /* uses first character of supplied padding */
            char paddingCharacter = value_text(1).charAt(0);
            int repeat = value_int(2);

            int padLength = repeat - text.length();
            if(padLength <= 0)
            {
               result(text);
            }

            char[] padding = new char[padLength];
            Array.fill(padding, paddingCharacter);
            result(text.append(new String(padding)));
        }
    });
  }
}

(未经测试,即兴发挥,不处理空值等,但应该概述这个想法......)

【讨论】:

  • 非常好,我不知道这是可能的!在我的特定情况下,我不确定是否要创建依赖于此 JDBC 驱动程序功能的 UDF。但总的来说,这是一个不错的解决方案
  • @LukasEder:是的,它在多功能库中的实用性稍差一些,但有一些可能性。如果它可以作为构建依赖项但不是运行时依赖项,那么您可以拥有一个特定于数据库的“初始化程序”类,如果连接类字符串匹配“org.sqlite”或其他内容,则会加载(通过反射)。但是,是的,这不是唯一的方法,如果您有更好的方法,您可以随时使用它们。
【解决方案4】:

她是使用 CASE 用前导零填充 0-9 的简单解决方案。

sqlite> select id,week,year from bulletin where id = 67;
67|2|2014

select id,CASE WHEN length(week) = 2 THEN week 
               ELSE '0'||week 
          END AS week,year from bulletin where id = 67;
67|02|2014

【讨论】:

  • 是的,它适用于将某些东西填充到固定长度。但即使这个固定长度是10,这个解决方案也会变得冗长......
【解决方案5】:

复制自http://verysimple.com/2010/01/12/sqlite-lpad-rpad-function/

-- the statement below is almost the same as
-- select lpad(mycolumn,'0',10) from mytable

select substr('0000000000' || mycolumn, -10, 10) from mytable

-- the statement below is almost the same as
-- select rpad(mycolumn,'0',10) from mytable

select substr(mycolumn || '0000000000', 1, 10) from mytable

【讨论】:

  • 这是一个优雅的解决方案 except (我相信),在值的长度 more 超过 10 的情况下......其中情况下,多余的左侧字符将被截断。例如,SELECT SUBSTR('00000' || 'ABCDEFG', -5, 5) 会产生“CDEFG”。这可能是非常不受欢迎且无声的行为
【解决方案6】:

@user610650 解决方案的更简单版本,使用 hex() 而不是 quote(),除了字符填充之外,还可以使用字符串填充:

X = padToLength
Y = padString
Z = expression

select
    Z ||
    substr(
        replace(
            hex(zeroblob(X)),
            '00',
            Y
        ),
        1,
        X - length(Z)
    );

【讨论】:

    【解决方案7】:

    我完全没有使用 SQLite 的经验,实际上我与 SQLite3 db 交互的时间只有不到三天。所以我不确定我的发现对你的要求有什么帮助。

    我正在玩一些有趣的项目,即拥有所有可能的 11 位电话号码(3 位运营商前缀 + 8 位用户号码)。我的目标是创建某种具有尽可能少的存储资源的数据库,但必须涵盖数据库上所有可能的数字。所以我为 8 位订户创建了一个表,另一个表包含 3 位公司前缀。最终数字将出现在连接两个表数据的视图中。让我专注于 LOAD 问题。由于订阅者表列是 INT,它是 0 到 99999999 条记录。订阅者数量少于 10000000 的简单加入失败;任何低于 10000000 的订阅者订阅 ID 号都会显示为 XXXprefix+11,其中预期为 XXX000000+11。

    在 SQLite 上使用 LPAD/RPAD 失败后,我找到了“SUBSTR”!

    看看下面的查询:

    CREATE TABLE subs_num (
    subscriber_num integer PRIMARY KEY
    );
    
    INSERT INTO subs_num values ('1');
    INSERT INTO subs_num values ('10');
    INSERT INTO subs_num values ('100');
    INSERT INTO subs_num values ('1000');
    
    SELECT subscriber_num from subs_num;
    
    SELECT SUBSTR('00000000' || subscriber_num, -8, 8) AS SUBSCRIB_ID FROM subs_num;
    

    现在我认为您可以使用 SUBSTR 来满足您的 LPAD/RPAD 需求。

    干杯!

    【解决方案8】:

    你也可以打印。

    sqlite> SELECT PRINTF('%02d',5);
    05
    sqlite> SELECT PRINTF('%04d%02d',25,5);
    002505
    sqlite> 
    

    【讨论】:

    • 仅对零填充数字有用。不适用于字符串:SELECT printf('%010s', '8')' 8'
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-19
    • 1970-01-01
    • 2017-01-22
    • 2020-10-16
    • 1970-01-01
    • 2021-01-20
    相关资源
    最近更新 更多