SQL准备语句如何通过多种可能的菜单选择进行选择?

Arm*_*rea 7 java sql jdbc

所以我有4个菜单选项(产品,位置,courseType和类别),所有这些都可以为null(使用JSF编程,但这应该与此问题无关,因为它是一个SQL问题).

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

如果用户将菜单项保留为null,则应搜索所有内容.

如果用户将1或2或3个菜单项留下信息,而另一个留空,则应相应地进行搜索.

我的问题是如何在没有附加到适当的sql语句的bean中的一堆if/then语句的情况下执行此操作?

或者是否有一个更好的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);
        } 
Run Code Online (Sandbox Code Playgroud)

等等,但我必须这样做16次,每个案例1为空而不是其他情况?必须有一个更聪明的方法(通过使用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();
Run Code Online (Sandbox Code Playgroud)

更新是的我不得不使用=""而不是为一个不同的mysql数据库是null(可能是不同的版本或东西)

Lui*_*oza 7

我假设你有一个NULL所选值的默认值(因为我不知道使用它的另一种方法PreparedStatement)'0'(仅作为一个例子).

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

SELECT *
FROM Courses
WHERE
    (product = ? or ? = '0')
    and (location = ?  or ? = '0')
    and (courseType = ? or ? = '0')
    and (category = ? or ? = '0')
Run Code Online (Sandbox Code Playgroud)

使用此方法,您可以利用该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');
Run Code Online (Sandbox Code Playgroud)

现在,相关的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);
    }
}
Run Code Online (Sandbox Code Playgroud)

使用MySQL 5.1.18测试.


jah*_*roy 1

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

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

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