当前位置: 首页 > 面试题库 >

这是PostgreSQL SQL引擎的错误,如何避免(解决方法)呢?

楮法
2023-03-14
问题内容

我正在解析文本文档并将其插入PostgreSQL
DB。我的代码是用Java编写的,并且使用JDBC进行数据库连接。将数据添加到数据库时,我遇到了一个非常奇怪的错误-
似乎在不可预测的时刻(主循环的迭代次数不同),Postgres看不到刚刚添加到表中的行,并且无法正确执行更新。

也许我做错了什么,所以也许有一种方法可以纠正我的代码?
还是PostgreSQL的严重错误,我应该将它发布在PostgreSQL主页上(作为错误报告)?

以下是 我正在做的事情和出了什么问题 的详细信息 。我已经简化了代码以隔离错误-
简化版不会解析任何文本,但会使用生成的单词进行模拟。我的问题结尾处 包含源文件 (java和sql)。

在我的问题的简化示例中,我有一个单线程代码,一个JDBC连接,3个表和很少的SQL语句(完整的Java源代码少于90行)。

主循环适用于“文档”-20个单词,后接doc_id(整数)。

  1. spb_word4obj清除缓冲区表,以便仅插入doc_id。
  2. 将单词插入缓冲区表(spb_word4obj),
  3. 然后将唯一的新单词插入表中 spb_word
  4. 最后-将文档的单词插入spb_obj_word-单词主体替换为spb_word(引用)中的单词ID 。

虽然迭代此循环一段时间(例如2,000或15,000次迭代-这是不可预测的),但由于SQL错误而失败-无法将null
word_id插入spb_word。手动重复此最后一次迭代不会产生任何错误,这使它变得更加奇怪。PostgreSQL似乎在记录插入和语句执行速度方面存在一些问题-
它会丢失一些数据,或者在稍加延迟后使其对后续语句可见。

生成单词的顺序是可重复的-每次运行代码时,它都会生成相同的单词顺序,但是每次代码失败时的迭代次数都是不同的。

这是我创建表的sql代码:

create sequence spb_word_seq;

create table spb_word (
  id bigint not null primary key default nextval('spb_word_seq'),
  word varchar(410) not null unique
);

create sequence spb_obj_word_seq;

create table spb_obj_word (
  id int not null primary key default nextval('spb_obj_word_seq'),
  doc_id int not null,
  idx int not null,
  word_id bigint not null references spb_word (id),
  constraint spb_ak_obj_word unique (doc_id, word_id, idx)
);

create sequence spb_word4obj_seq;

create table spb_word4obj (
  id int not null primary key default nextval('spb_word4obj_seq'),
  doc_id int not null,
  idx int not null,
  word varchar(410) not null,
  word_id bigint null references spb_word (id),
  constraint spb_ak_word4obj unique (doc_id, word_id, idx),
  constraint spb_ak_word4obj2 unique (doc_id, word, idx)
);

Java代码在这里-它可能只是被执行(它具有静态main方法)。

package WildWezyrIsAstonished;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class StrangePostgresBehavior {

    private static final String letters = "abcdefghijklmnopqrstuvwxyz膮膰臋艂艅贸艣藕偶";
    private static final int llen = letters.length();
    private Connection conn;
    private Statement st;
    private int wordNum = 0;

    public void runMe() throws Exception {
        Class.forName("org.postgresql.Driver");

        conn = DriverManager.getConnection("jdbc:postgresql://localhost:5433/spb",
                "wwspb", "*****");
        conn.setAutoCommit(true);
        st = conn.createStatement();

        st.executeUpdate("truncate table spb_word4obj, spb_word, spb_obj_word");

        for (int j = 0; j < 50000; j++) {

            try {
                if (j % 100 == 0) {
                    System.out.println("j == " + j);
                }

                StringBuilder sb = new StringBuilder();

                for (int i = 0; i < 20; i++) {
                    sb.append("insert into spb_word4obj (word, idx, doc_id) values ('"
                            + getWord() + "'," + i + "," + j + ");\n");
                }
                st.executeUpdate("delete from spb_word4obj where doc_id = " + j);

                st.executeUpdate(sb.toString());

                st.executeUpdate("update spb_word4obj set word_id = w.id "
                        + "from spb_word w "
                        + "where w.word = spb_word4obj.word and doc_id = " + j);

                st.executeUpdate("insert into spb_word (word) "
                        + "select distinct word from spb_word4obj "
                        + "where word_id is null and doc_id = " + j);

                st.executeUpdate("update spb_word4obj set word_id = w.id "
                        + "from spb_word w "
                        + "where w.word = spb_word4obj.word and "
                        + "word_id is null and doc_id = " + j);

                st.executeUpdate("insert into spb_obj_word (word_id, idx, doc_id) "
                        + "select word_id, idx, doc_id from spb_word4obj "
                        + "where doc_id = " + j);
            } catch (Exception ex) {
                System.out.println("error for j == " + j);
                throw ex;
            }
        }
    }

    private String getWord() {
        int rn = 3 * (++wordNum + llen * llen * llen);
        rn = (rn + llen) / (rn % llen + 1);
        rn = rn % (rn / 2 + 10);

        StringBuilder sb = new StringBuilder();
        while (true) {
            char c = letters.charAt(rn % llen);
            sb.append(c);
            rn /= llen;
            if (rn == 0) {
                break;
            }
        }

        return sb.toString();
    }

    public static void main(String[] args) throws Exception {
        new StrangePostgresBehavior().runMe();
    }
}

再说一遍:是我做错了什么(到底是什么?)还是PosgreSQL SQL Engine中的严重缺陷(比-有没有解决方法)?

我已经在Windows Vista机器上进行了以下测试:Java 1.6 / PostgreSQL 8.3.3和8.4.2 / JDBC
PostgreSQL驱动程序postgresql-8.2-505.jdbc3和postgresql-8.4-701.jdbc4。所有组合都会导致上述错误。为了确保这不是我的机器上的东西,我已经在其他机器上的类似环境中进行了测试。

更新: 正如Depesz所建议的,我已经启用了Postgres日志记录。以下是最新执行的sql语句:

2010-01-18 16:18:51 CETLOG:  execute <unnamed>: delete from spb_word4obj where doc_id = 1453
2010-01-18 16:18:51 CETLOG:  execute <unnamed>: insert into spb_word4obj (word, idx, doc_id) values ('ouc',0,1453)
2010-01-18 16:18:51 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('rbjb',1,1453)
2010-01-18 16:18:51 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('pvr',2,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('gal',3,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('cai',4,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('偶jg',5,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('egf',6,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('艣ne',7,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('臋臋d',8,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('lnd',9,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('cbd',10,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('d膮c',11,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('艂rc',12,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('zm艂',13,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('zxo',14,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('o膰j',15,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('zlh',16,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('l艅f',17,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('c贸e',18,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: 
  insert into spb_word4obj (word, idx, doc_id) values ('uge',19,1453)
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: update spb_word4obj set word_id = w.id from spb_word w where w.word = spb_word4obj.word and doc_id = 1453
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: insert into spb_word (word) select distinct word from spb_word4obj where word_id is null and doc_id = 1453
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: update spb_word4obj set word_id = w.id from spb_word w where w.word = spb_word4obj.word and word_id is null and doc_id = 1453
2010-01-18 16:18:52 CETLOG:  execute <unnamed>: insert into spb_obj_word (word_id, idx, doc_id) select word_id, idx, doc_id from spb_word4obj where doc_id = 1453
2010-01-18 16:18:52 CETERROR:  null value in column "word_id" violates not-null constraint
2010-01-18 16:18:52 CETSTATEMENT:  insert into spb_obj_word (word_id, idx, doc_id) select word_id, idx, doc_id from spb_word4obj where doc_id = 1453

现在-检查表中错误的代码spb_word4obj

select * 
from spb_word4obj w4o left join spb_word w on w4o.word = w.word
where w4o.word_id is null

它显示了两个词:'gal', 'zxo'引起了问题。但是…在spb_word表中可以找到它们-刚刚从日志中插入了sql语句(包含在上面)。

因此-JDBC驱动程序不是问题,而是Postgres本身?

UPDATE2: 如果我膮膰臋艂艅贸艣藕偶从生成的单词中删除波兰国家字符(),则没有错误-
代码执行了所有50,000次迭代。我已经测试了几次。因此,对于这一行:

    private static final String letters = "abcdefghijklmnopqrstuvwxyz";

没有错误,一切似乎都很好,但是有了这一行(或上面完整源代码中的原始行):

    private static final String letters = "膮膰臋艂艅贸艣藕偶jklmnopqrstuvwxyz";

我收到上述错误。

UPDATE3: 我刚刚发布了类似的问题,但没有使用 Java-
完全移植到了纯plpgsql,请看这里:为什么该代码在PostgreSQL中失败,以及如何解决(解决方法)?它是Postgres
SQL引擎缺陷吗?。现在我知道它与Java不相关-
这是Postgres本身的问题。


问题答案:

我对该问题的进一步调查显示,该问题与纯Postgres
SQL有关,我开发了纯plpgsql版本,该版本是上述代码的一对一端口。对纯plpgsql的重新提出的问题在这里:为什么此代码在PostgreSQL中失败,以及如何解决(变通方法)?它是Postgres
SQL引擎缺陷吗?。

所以-这不是Java / JDBC相关的问题。

此外,我设法简化了测试代码-现在它使用一个表。简化的问题发布在pgsql-
bugs邮件列表上:http : //archives.postgresql.org/pgsql-
bugs/2010-01/msg00182.php。确认发生在其他机器上(不仅是我的机器)。

解决方法是:将数据库排序规则从波兰语更改为标准“ C”。使用“
C”排序规则时,没有错误。但是,如果不使用波兰语归类,则波兰语单词的排序不正确(相对于波兰语国家字符),因此问题应在Postgres本身中解决。



 类似资料:
  • 我正在将oracle从10升级到12,对于这个特定的项目,我遇到了以下错误: 似乎它没有识别我在DataSource上添加的配置: 我们使用的是Java 8,我们对代码有这些依赖关系: 问题是它正在为另一个项目工作(与这个项目具有相同的结构,但由于某种原因它在这里不起作用) 是否有任何遗漏或我可以找到问题并解决问题的地方?

  • 问题内容: 我试图写一个近似平方根的函数(我知道有数学模块……我想自己做),但我被浮点运算搞砸了。如何避免这种情况? 使用它会产生以下结果: 我意识到我可以使用,但是我希望能够做到这一点非常准确。我希望能够计算出6或7位数字。如果我四舍五入,那将是不可能的。我想了解如何在Python中正确处理浮点计算。 问题答案: 这实际上与Python无关- 使用硬件的二进制浮点算法,您会在任何语言中看到相同的

  • 配置根项目“我的应用程序”时出现问题。 未能解析配置“:classpath”的所有工件。无法解析com.android.tools.build:gradle:3.6.2。project:脱机模式下没有com.android.tools.build:gradle:3.6.2的缓存版本。脱机模式下没有com.android.tools.build:gradle:3.6.2的缓存版本。 这是我的错误,我

  • 本文向大家介绍Django 解决由save方法引发的错误,包括了Django 解决由save方法引发的错误的使用技巧和注意事项,需要的朋友参考一下 最近项目中的资产的任务状态频频出现问题,查看日志文件,看代码逻辑,也没发现什么具体的错误,总是过段时间就会出现一个表的字段没有更新的问题,很头疼。 开始时,觉得是没有添加事务,所以同时更新两个不同的表,其中一个表的内容没有写进去;加了事务后,又出现这种

  • 问题内容: 我正在将JTOpen JDBC驱动程序用于DB2通用数据库。除了简单的语句,我对SQL的经验很少。 从这个问题中,我看到尝试“在事务期间插入/更新非日记表中的行”时,抛出了我正在获取的错误(SQL7008)。 根据项目负责人的说法,我们的数据库未记录在日志中,因此不会很快出现(不要问我为什么,我不是DBA)。但是,我正在一个项目中,几乎一劳永逸(而不是每次调用一次执行都会自动提交)的功

  • 问题内容: 我有此错误信息: 消息8134,级别16,状态1,第1行除以零错误。 编写SQL代码的最佳方法是什么,这样我就再也看不到此错误消息了? 我可以执行以下任一操作: 添加一个where子句,这样我的除数永远不会为零 或者 我可以添加一个case语句,以便对零进行特殊处理。 使用子句的最佳方法是吗? 有没有更好的方法,或者如何执行? 问题答案: 为了避免出现“被零除”错误,我们对此进行了如下