简体   繁体   English

如何使用 SQLite 模拟 LPAD/RPAD

[英]How to emulate LPAD/RPAD with SQLite

I'm curious about how to emulate RPAD and LPAD functions for SQLite, formally, in the most general way.我很好奇如何以最一般的方式正式模拟 SQLite 的 RPAD 和 LPAD 功能。 The goal is to be able to do目标是能够做到

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

For non-constant table columns column , character , repeat .对于非常量表列columncharacterrepeat If character and repeat were known constants, then this would be a good, viable solution:如果characterrepeat是已知的常量,那么这将是一个很好的、可行的解决方案:

But what if the above should be executed like this:但是如果上面应该这样执行怎么办:

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

How could this LPAD function be generally emulated?这个LPAD function 通常如何被仿真? I'm lost with the possibilities:我迷失了各种可能性:

A related question:一个相关的问题:

Copied from http://verysimple.com/2010/01/12/sqlite-lpad-rpad-function/复制自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

You could also PRINTF.您也可以使用 PRINTF。

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

A simpler version of @user610650's solution, using hex() instead of quote(), and works with string padding in addition to char padding: @user610650 解决方案的更简单版本,使用 hex() 而不是 quote(),除了字符填充外,还可以使用字符串填充:

X = padToLength
Y = padString
Z = expression

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

Here's more nastiness for you:这里对你来说更讨厌:

X = padToLength
Y = padString
Z = expression

RPAD (for LPAD, Z is concatenated after instead): 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))
    )

Examples:例子:

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 is meant to be quite lightweight, so I have to disagree somewhat with your comment about being "surprised" by the lack of functionality. Sqlite 本来就是轻量级的,所以我不得不对你对缺乏功能感到“惊讶”的评论有些不同意。 However, I agree that there should be a simpler way to do padding, if only because the trim functions exist.但是,我同意应该有一种更简单的方法来进行填充,如果只是因为存在trim功能。

A JDBC/custom functions approach (may not be suitable in your exact case, but might be able to be adapted). JDBC/自定义函数方法(可能不适合您的具体情况,但可能能够适应)。 Uses inspiration from SqliteJDBC Custom Functions and the rightPad and leftPad functions from Apache commons.lang.StringUtils:使用来自SqliteJDBC 自定义函数以及来自 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)));
        }
    });
  }
}

(Untested, off the cuff, doesn't handle nulls, etc, but should outline the idea...) (未经测试,即兴发挥,不处理空值等,但应该概述这个想法......)

Her's a simple solution to pad 0-9 with a leading zero using CASE.她是使用 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

I absolutely have no experience with SQLite, actually my time of interacting with SQLite3 db less then three days only.我绝对没有 SQLite 的经验,实际上我与 SQLite3 db 交互的时间只有不到三天。 So I am not sure my findings could help anything to your requirement.所以我不确定我的发现对你的要求有什么帮助。

I am playing with some fun project of having all possible 11 digit phone number (3 digit operator prefix + 8 digit subscriber number).我正在玩一些有趣的项目,即拥有所有可能的 11 位电话号码(3 位运营商前缀 + 8 位用户号码)。 My target was to create some kind of database with minimum possible storage resource but must have to cover every possible number on database.我的目标是创建某种具有尽可能少的存储资源的数据库,但必须涵盖数据库上所有可能的数字。 So I created one table for 8 digit subscriber and another table contain 3 digit company prefix.所以我为 8 位订户创建了一个表,另一个表包含 3 位公司前缀。 Final number will come up on view joining two table data.最终数字将出现在连接两个表数据的视图中。 Let me focus on LOAD Problem.让我专注于 LOAD 问题。 As subscriber table column is INT, it is 0 to 99999999 individual record.由于订阅者表列是 INT,它是 0 到 99999999 条记录。 Simple join fail for subscriber number having less then 10000000;用户数少于 10000000 的简单加入失败; any subscribers subscription id number valued under 10000000 shows up XXXprefix+11 where expecting XXX000000+11.任何低于 10000000 的订阅者订阅 ID 号都会显示为 XXXprefix+11,其中预期为 XXX000000+11。

After failing with LPAD/RPAD on SQLite, I found "SUBSTR"!在 SQLite 上使用 LPAD/RPAD 失败后,我找到了“SUBSTR”!

Have a look on query bellow:看看下面的查询:

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;

Now I think you can use SUBSTR for your LPAD/RPAD needs.现在我认为您可以使用 SUBSTR 来满足您的 LPAD/RPAD 需求。

Cheers!干杯!

Maybe like this:也许是这样的:

  • LPAD(@orig_text, @padding_char, @padding_length) : 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) : 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 )

printf works for spaces, too: printf 也适用于空格:

SELECT 'text lpad 20 "'||printf("%020s", 'text')||'"'  paddedtxt UNION
SELECT 'text rpad 20 "'||printf("%-020s", 'text')||'"' paddedtxt UNION
SELECT 'num  lpad 20 "'||printf("%020d", 42)||'"'      paddedtxt

results to结果到

num  lpad 20 "00000000000000000042"
text lpad 20 "                text"
text rpad 20 "text                "

Goal: Use available sqlite functions to mimic ltrim目标:使用可用的 sqlite 函数来模拟 ltrim

Approach: (1) Use concat function "||"做法:(1)使用concat function "||" to add zeroes (or whatever the desired pad character is) to the left of the string, adding the number of zeroes equal to the number of characters you want left over, then adding a zero to the right.在字符串的左侧添加零(或任何所需的填充字符),添加等于您想要剩余的字符数的零,然后在右侧添加零。 (2) Use substring to keep the desired number of characters. (2) 使用 substring 保持所需的字符数。

Example: To pad page_number which was an integer with zeroes on the left to end up with five characters zero-filled to the left:示例:将 page_number 填充为 integer 左侧为零,以在左侧填充五个字符零:

select select

substring(('00000' || cast(page_number as text) || '0'), -1, -5) substring(('00000' || cast(page_number as text) || '0'), -1, -5)

from pages where classification = xxx'来自分类 = xxx' 的页面

Comment: substring seemed to ignore the last character when starting from -1, that's why I added a zero to the right - so it can be ignored.评论:substring 从 -1 开始时似乎忽略了最后一个字符,这就是为什么我在右边添加了一个零 - 所以它可以被忽略。 There's probably a more elegant way to do that.可能有一种更优雅的方式来做到这一点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM