【问题标题】:SQL prepared statement how to select via multiple possible menu selections?SQL 准备语句如何通过多个可能的菜单选择进行选择?
【发布时间】:2013-02-27 20:36:00
【问题描述】:

所以我有 4 个菜单选择(产品、位置、课程类型和类别),所有这些都可以为空(使用 JSF 编程,但这应该与这个问题无关,因为它是一个 SQL 问题)。

菜单选择将向托管 bean 发送用户选择的变量,并使用准备好的语句使用用户选择的菜单中的信息(如果有)搜索数据库表。

如果用户让菜单项为空,它应该搜索所有内容。

如果用户留下 1 或 2 或 3 个带有信息的菜单项,而另一个为空,则应进行相应的搜索。

我的问题是,如果 bean 中没有一堆 if/then 语句附加到每个相应的 sql 语句,我该如何做到这一点?

或者有没有更好的 sql 语句我可以做到这一切?

我在 Java 中使用准备好的语句。

我试过了:

if (product != null && location != null && courseType != null && category != null) {
            pstmt = conn.prepareStatement("select * FROM Courses WHERE "
                    + "product = ? "
                    + "and location = ? "
                    + "and courseType = ? "
                    + "and category = ?");

            pstmt.setString(1, product);
            pstmt.setString(2, location);
            pstmt.setString(3, courseType);
            pstmt.setString(4, category);
        } else if (product == null && location != null && courseType != null && category != null) {
            pstmt = conn.prepareStatement("select * FROM Courses WHERE "
                    + "location = ? "
                    + "and courseType = ? "
                    + "and category = ?");


            pstmt.setString(1, location);
            pstmt.setString(2, courseType);
            pstmt.setString(3, category);
        } 

etc 等等,但是对于每个 1 为空而不是其他情况的情况,我必须这样做 16 次? 一定有更聪明的方法(要么使用 1 条 sql 语句,要么只使用几条 java if/then 语句?)

感谢 Luiggi Mendoza 更新!我的代码是这样工作的:

pstmt = conn.prepareStatement("select * FROM Courses WHERE " 
         + "(product = ? or ? is null) " 
         + "and (location = ? or ? is null) " 
         + "and (courseType = ? or ? is null)" 
         + "and (category = ? or ? is null)"); 

         pstmt.setString(1, product);
         pstmt.setString(2, product);
         pstmt.setString(3, location);
         pstmt.setString(4, location);
         pstmt.setString(5, courseType);
         pstmt.setString(6, courseType);
         pstmt.setString(7, category);
         pstmt.setString(8, category);


        rset = pstmt.executeQuery();

更新是的,对于不同的 mysql 数据库(可能是不同的版本或其他东西),我必须使用 = "" 而不是 null

【问题讨论】:

    标签: java sql jdbc


    【解决方案1】:

    我假设您的NULL 选定值有一个默认值(因为我不知道使用PreparedStatement 的另一种方法)作为'0'(仅作为示例)。

    知道了这一点,我倾向于使用这种SQL:

    SELECT *
    FROM Courses
    WHERE
        (product = ? or ? = '0')
        and (location = ?  or ? = '0')
        and (courseType = ? or ? = '0')
        and (category = ? or ? = '0')
    

    通过这种方法,您可以利用 IF 的用法并让数据库来处理工作。唯一不好的一点是你应该将每个变量设置两次(这辈子没有什么是免费的=\)。


    编辑:我已根据您的要求进行了测试。先上SQL表和内容:

    create table pruebaMultiSelect
    (id int primary key auto_increment,
    nombre varchar(50),
    tipo varchar(10),
    categoria varchar(10));
    
    insert into pruebaMultiSelect values
    (null, 'Luiggi', 'A', 'X');
    
    insert into pruebaMultiSelect values
    (null, 'Thomas', 'A', 'Y');
    
    insert into pruebaMultiSelect values
    (null, 'Jose', 'B', 'X');
    
    insert into pruebaMultiSelect values
    (null, 'Trina', 'B', 'Y');
    

    现在,相关的 Java 代码:

    public class DBTest {
    
        public void testPreparedStatement(String p1, String p2) throws SQLException {
            Connection con = null;
            PreparedStatement pstmt = null;
            ResultSet rs = null;
            try {
                con = getConnection();
                pstmt = con.prepareStatement("SELECT * FROM pruebaMultiSelect WHERE (tipo = ? OR ? = 'EMPTY') "
                        + "AND (categoria = ? OR ? = 'EMPTY')");
                pstmt.setString(1, p1);
                pstmt.setString(2, p1);
                pstmt.setString(3, p2);
                pstmt.setString(4, p2);
                rs = pstmt.executeQuery();
                while (rs.next()) {
                    System.out.printf("%5d %15s\n", rs.getInt(1), rs.getString(2));
                }
            } catch(ClassNotFoundException e) {
                e.printStackTrace();
            } finally {
                rs.close();
                pstmt.close();
                con.close();
            }
        }
    
        public Connection getConnection() throws SQLException, ClassNotFoundException {
            Class.forName("com.mysql.jdbc.Driver");
            return DriverManager.getConnection("jdbc:mysql://localhost:3306/luiggi_test", "user", "password");
        }
    
        public static void main(String[] args) throws SQLException {
            //this will return all the data
            String p1 = "EMPTY";
            String p2 = "EMPTY";
            new DBTest().testPreparedStatement(p1, p2);
        }
    }
    

    使用 MySQL 5.1.18 测试。

    【讨论】:

    • 是的,实际上我之前尝试过,但效果不佳。我使用preparedstatement这样做: pstmt = conn.prepareStatement("select * FROM Courses WHERE " + "product = ? or product is null " + "and (location = ? or location is null) " + "and (courseType = ? or courseType is null)" + "and (category = ? or category is null)");
    • 这就是为什么我不建议使用NULL,而是使用'0''EMPTY' 之类的默认值(或NULL 的其他默认值)。
    • 嗯,这种方式似乎行不通,可能是因为准备好的语句不会让我做 '0' 或 'EMPTY'。 Java 类preparedstatement 将自己的信息发送到? 指定的SQL 服务器
    • @ArmandoPerea 答案已更新,现在它显示了一个示例(不过使用不同的表格)。
    • 哦,感谢您的编辑,我会尝试一下。我认为我的 pstmt.setString 没有正确执行。我会让你知道我是否能正常工作....
    【解决方案2】:

    我倾向于使用这样的辅助方法构建动态查询:

    StringBuilder query = new StringBuidler();
    query.append("SELECT foo, bar FROM baz");
    query.append(buildProductClause(product));
    query.append(buildLocationClause(location));
    query.append(buildCourseTypeClause(courseType));
    // etc...
    

    如果您仍想使用准备好的语句,您可以使用成员字段来构建参数数组。

    【讨论】:

    • 这是一个很好的方法,但我发现当您想要/需要添加新列进行检查时很难维护(更糟糕的是,当您必须在 FROM 部分添加新表时) .仅当应动态填充 FROMORDER BY 列时,我才尝试使用此方法。
    猜你喜欢
    • 1970-01-01
    • 2017-10-23
    • 1970-01-01
    • 1970-01-01
    • 2021-05-12
    • 2016-08-12
    • 1970-01-01
    • 1970-01-01
    • 2018-08-11
    相关资源
    最近更新 更多