【问题标题】:Column in field list is ambiguous字段列表中的列不明确
【发布时间】:2020-09-01 07:06:05
【问题描述】:

Reservation:

我想给客人他必须支付的金额,我有一个程序

CREATE DEFINER=`root`@`localhost` PROCEDURE `CalculateTotal`(IN ID int ,OUT total float)
BEGIN
SELECT (DATEDIFF(reservation.endDate, reservation.startDate) * room.price)
INTO total
FROM (select * from guest Where guestID = ID) As R , Room, Reservation
WHERE Room.roomNumber = Reservation.roomNumber AND Reservation.guestID =
R.guestID;
END
System.out.println("Enter your ID guest:");
int gID = keyboard.nextInt();
rs = stmt.executeQuery("Select guestID from reservation where guestID = "+gID+";");
if(rs.next())
   {
   stmt.executeQuery("call calculateTotal(" + gID + ", @result);");
   rs = stmt.executeQuery("Select firstName, lastName, roomNumber, class, price, startDate, endDate,  @result as TotalPrice From (Guest join (Reservation join Room on (Reservation.roomNumber = Room.roomNumber)) on Reservation.guestID = Guest.guestID ) where reservation.guestID = "+gID+";");
   while(rs.next())
       System.out.println("Name: "+ rs.getString("firstName")+" "+ rs.getString("lastName")+ "  Room Number:"+ rs.getInt("roomNumber") + "  Class:"+ rs.getString("class")+ "  Price:"+ rs.getFloat("price")+ rs.getDate("startDate")+"  EndDate: "+ rs.getDate("endDate")+ "  Total Price: "+ rs.getFloat("TotalPrice") + "р.");
   }

结束我有一个错误

java.sql.SQLIntegrityConstraintViolationException: Column 'roomNumber' in field list is ambiguous
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:117)
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
    at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
    at com.mysql.cj.jdbc.StatementImpl.executeQuery(StatementImpl.java:1200)
    at com.jdbc.Main.main(Main.java:172)

172 原始是rs = stmt.executeQuery("Select firstName, lastName...

可能会以某种方式改进程序和代码?

【问题讨论】:

  • 在我看来,您需要在您发布的代码中的所有 SQL 语句中为所有表列名加上表名前缀。
  • 你真的需要一个程序吗?
  • 在以"Select firstName, lastName, roomNumber, class, price... 开头的查询中,将表别名放在所有这些列上。 roomNumber 出现在连接中的多个表中。你必须告诉 SQL 引擎你想要返回哪一个。

标签: java mysql exception stored-procedures


【解决方案1】:

正如@Abra 和@Eric Brandt 指出的那样,例外情况是您正在查询多个表,并且您需要在字段前面加上表名或别名,以防止引用不明确的列(例如列在不同的表中具有相同的名称)

既然您希望改进代码,我会提出一些建议。

正如@Strawberry 所建议的,如果最终目标只是查询预订,则不需要该过程。虽然您可能打算扩展该过程的功能,但恕我直言,通常最好避免使用它们来进行简单的计算、聚合等,这些可以用纯 SQL 完成。

在使用用户输入时将字符串连接在一起形成 SQL 可能很危险。在这种情况下是安全的,因为keyboard.nextInt() 可以防止 SQL 注入攻击,但是您可以考虑使用准备好的语句来获得额外的保护,准备好的语句也可以具有其他优势。例如,某些数据库会缓存运行计划,这在更复杂的查询将运行多次时非常重要。

您可能总是希望在联接中使用“on”子句。否则你会得到一个交叉连接,这可能不是你想要的。

一旦您超越了简单的示例,在 Java 代码中嵌入 SQL 可能会变得乏味,而将其作为资源外部化会非常有用,并且可以设置为模板化 SQL,或者使用变体来处理不同数据库之间的怪癖(例如MySQL vs Oracle vs Postgres 等)。

对于诸如自然键与代理键、lowerCamel 列与低蛇形列、何时使用或避免存储过程等问题,有一大堆意见,但与其尝试详尽的“最佳实践” " 列表,无论如何这只是我的看法,这是一个替代示例,与您的原始示例相差不远(即忽略 ORM 等)

import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;

public class Hotel {

    private static Connection connection;
    private static ResultSet rs;
    private static Scanner keyboard;
    private static Statement stmt;

    public static void main(String[] args) throws Exception {
        if (args.length != 3) {
            // Note: Don't pass passwords on the command line in real life.
            System.err.println("Usage: java Hotel <dbUrl> <dbUser> <dbPwd>");
            return;
        }
        // Initial set up
        keyboard = new Scanner(System.in);
        connection = DriverManager.getConnection(args[0], args[1], args[2]);
        stmt = connection.createStatement();
        // Do it the original way (from the post).
        // orig();
        // Do it a slightly different way.
        anotherWay();
    }

    public static void orig() throws Exception {
        System.out.println("(orig) Enter your ID guest:");
        int gID = keyboard.nextInt();
        rs = stmt.executeQuery("Select guestID from Reservation where guestId = " + gID + ";");
        if (rs.next()) {
            stmt.executeQuery("call calculateTotal(" + gID + ", @result);");
            rs = stmt.executeQuery(
                    "Select firstName, lastName, Reservation.roomId as roomNumber, class, price, startDate, endDate,  @result as TotalPrice From (Guest join (Reservation join Room on (Reservation.roomId = Room.id)) on Reservation.guestID = Guest.id ) where Reservation.guestID = "
                            + gID + ";");
            while (rs.next()) 
                System.out.println("  Name: " + rs.getString("firstName") + " " + rs.getString("lastName")
                + "\n  Room Number: " + rs.getInt("roomNumber") + "\n  Class: " + rs.getString("class")
                + "\n  Price: " + String.format("%.2f", rs.getFloat("price")) + "\n  Start Date: "
                + rs.getDate("startDate") + "\n  EndDate: " + rs.getDate("endDate") + "\n  Total Price: "
                + String.format("%.2f", rs.getFloat("TotalPrice")));

        }
    }

    public static void anotherWay() throws Exception {
        System.out.println("(anotherWay) Enter your guest ID:");
        int guestId = keyboard.nextInt();
        // Get the SQL as a String.  Normally this would be standardized into
        // a utility, so it wouldn't be so messy.
        String sql = new String(
                Files.readAllBytes(Paths.get(Hotel.class.getResource("select_reservation.sql").toURI())));
        // Prepare the statement
        PreparedStatement ps = connection.prepareStatement(sql);

        // Set the parameter
        ps.setInt(1, guestId);
        ResultSet rs = ps.executeQuery();
        while (rs.next()) {
            printResult(rs);
        }
        rs.close();
        ps.close();
    }

    public static void printResult(ResultSet rs) throws Exception {
        System.out.println(
                "  Name: " + rs.getString("firstName") + " " + rs.getString("lastName") 
                + "\n  Room Number: "   + rs.getInt("roomNumber") 
                + "\n  Class: " + rs.getString("class") 
                + "\n  Price: " + String.format("%.2f", rs.getFloat("price")) 
                + "\n  Start Date: " + rs.getDate("startDate")
                + "\n  EndDate: " + rs.getDate("endDate") 
                + "\n  Room Charge: " + String.format("%.2f", rs.getFloat("roomCharge"))
                + "\n  Extra: " + String.format("%.2f", rs.getFloat("extra"))
                + "\n  Total Price: " + String.format("%.2f", rs.getFloat("totalPrice"))
                );
    }
}

这样运行(现实生活中不要在命令行使用密码)...

java Hotel &lt;url&gt; &lt;user&gt; &lt;pwd&gt;

guest 1 的输出应如下所示...

(anotherWay) Enter your guest ID:
1
  Name: Alice Smith
  Room Number: 1
  Class: premium
  Price: 100.00
  Start Date: 2020-05-15
  EndDate: 2020-05-22
  Room Charge: 700.00
  Extra: 350.00
  Total Price: 1050.00

这给客人 2...

(anotherWay) Enter your guest ID:
2
  Name: Bob Jones
  Room Number: 2
  Class: base
  Price: 75.00
  Start Date: 2020-05-15
  EndDate: 2020-05-22
  Room Charge: 525.00
  Extra: 0.00
  Total Price: 525.00 

这是外部化的 SQL...

select
    Guest.firstName,
    Guest.lastName,
    Room.id as roomNumber,
    Room.class,
    Room.price,
    Reservation.startDate,
    Reservation.endDate,
    (DATEDIFF(Reservation.endDate, Reservation.startDate) * Room.price) as roomCharge,
    IFNULL(extra.charges,0) as extra,
    ((DATEDIFF(Reservation.endDate, Reservation.startDate) * Room.price)
        + IFNULL(extra.charges,0)) as totalPrice
from    
    Reservation
    inner join Guest on Reservation.guestId = Guest.id
    inner join Room on Reservation.roomId = Room.id
    left join ( -- Use subquery to calculate extra charges.
        select  -- Could be more efficient by specifying key.
            guestId,
            sum(price) as charges
        from
            RoomService
            inner join Service on RoomService.serviceId = Service.id
        group by
            guestId) extra on extra.guestId = Guest.id
where
    Reservation.guestId = ?;

如果您想尝试一下,这里有一个完整的 MySQL 架构...

-- Make it idempotent
drop database if exists hotel;
create database hotel;

-- Create the tables (using lowerCamel cols)
CREATE TABLE hotel.Guest (
    id int AUTO_INCREMENT ,
    firstName varchar(40),
    lastName varchar (40),
PRIMARY KEY (id)
);
CREATE TABLE hotel.Room (
    id int AUTO_INCREMENT ,
    class varchar(40),
    price decimal(13,2),
PRIMARY KEY (id)
);
CREATE TABLE hotel.Reservation (
    id int AUTO_INCREMENT ,
    guestId int,
    roomId int,
    startDate date,
    endDate date,
PRIMARY KEY (id)
);

CREATE TABLE hotel.Service (
    id int AUTO_INCREMENT ,
    name varchar(40),
    price decimal(13,2),
PRIMARY KEY (id)
);

CREATE TABLE hotel.RoomService (
    id int AUTO_INCREMENT ,
    guestId int,
    roomId int,
    serviceId int,
PRIMARY KEY (id)
);

INSERT INTO hotel.Guest (id,firstName,lastName) VALUES (1,'Alice','Smith');
INSERT INTO hotel.Guest (id,firstName,lastName) VALUES (2,'Bob','Jones');
INSERT INTO hotel.Guest (id,firstName,lastName) VALUES (3,'Mallory','Adams');

INSERT INTO hotel.Room (id,class,price) VALUES (1,'premium',100.00);
INSERT INTO hotel.Room (id,class,price) VALUES (2,'base',75.00);
INSERT INTO hotel.Room (id,class,price) VALUES (3,'budget',50.00);

INSERT INTO hotel.Reservation (id,guestId,roomId,startDate,endDate) VALUES (1,1,1,'2020-05-15','2020-05-22');
INSERT INTO hotel.Reservation (id,guestId,roomId,startDate,endDate) VALUES (2,2,2,'2020-05-15','2020-05-22');

INSERT INTO hotel.Service (id,name,price) VALUES (1,'WIFI',100.00);
INSERT INTO hotel.Service (id,name,price) VALUES (2,'Safe',100.00);
INSERT INTO hotel.Service (id,name,price) VALUES (3,'Washing clothes',450.00);
INSERT INTO hotel.Service (id,name,price) VALUES (4,'Food delivery',250.00);

INSERT INTO hotel.RoomService (id,guestId,roomId,serviceId) VALUES (1,1,1,1);
INSERT INTO hotel.RoomService (id,guestId,roomId,serviceId) VALUES (2,1,1,4);

DELIMITER //
CREATE DEFINER=`root`@`localhost` PROCEDURE hotel.CalculateTotal(IN ID int ,OUT total float)
BEGIN
    SELECT 
        (DATEDIFF(Reservation.endDate, Reservation.startDate) * Room.price)
    INTO 
        total
    FROM 
        (select * from Guest Where id = ID) As R , 
        Room, 
        Reservation
WHERE 
    Room.id = Reservation.roomId 
    AND Reservation.guestId = R.id;
END //

DELIMITER ;

【讨论】:

  • 请帮我修改这个程序。有 2 个表 Services 和 Room_service(截图:link)。并且需要在Calculate Total中添加服务的金额
  • 从技术上讲,这 2 个新表格中的信息不足以将房费添加到账单中,因为“room_service”仅与房间号相关,而不是日期或 guestId(即客人可能不会当时一直住在房间里)。如果您要创建另一个表,例如“visit”,您可以从该表连接到 visitId 上的“room_service”,然后从“room_service.service_id”连接到“services”以获取额外费用,该费用可以添加到totalPrice 的计算。
  • 不可以,房间号本身不能点服务,客人是填在某个房间号里的,客人被赶出酒店的时候才算计费,所以不需要绑定服务对客人也是如此。我得到了这样一个程序,似乎是正确的:也许通过join可以以某种方式实现,但我不明白如何
  • SELECT (DATEDIFF(reservation.endDate, reservation.startDate) * room.price + sum(services.price)) INTO total FROM (select * from guest Where guestID = ID) As R , Room, Reservation, Services, Room_service WHERE Room.roomNumber = Reservation.roomNumber AND Reservation.guestID = R.guestID AND Room.roomNumber = Room_service.roomNumber AND Room_service.roomNumber = Reservation.roomNumber AND Services.service_id = Room_service.service_id;
  • 好的,我修改了架构以包含类似的 Service 和 RoomService 表,并汇总每位客人的额外费用。请注意,如果您使用聚合函数,如 SUM(),则必须按剩余字段分组。
猜你喜欢
  • 2020-07-30
  • 1970-01-01
  • 2011-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多