简体   繁体   English

字段列表中的列不明确

[英]Column in field list is ambiguous

Table Reservation :餐桌Reservation 在此处输入图像描述

I want to give the guest the amount he has to pay and I have a procedure我想给客人他必须支付的金额,我有一个程序

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") + "р.");
   }

End I have an error结束我有一个错误

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 raw is rs = stmt.executeQuery("Select firstName, lastName... 172 原始是rs = stmt.executeQuery("Select firstName, lastName...

May be somehow improve the procedure and code?可能以某种方式改进程序和代码?

The exception, as @Abra and @Eric Brandt point out, is that you're querying multiple tables, and you would need to prefix the fields with the table names, or aliases, to prevent references to ambiguous columns (eg columns that have the same name in different tables)正如@Abra 和@Eric Brandt 指出的那样,例外情况是您正在查询多个表,并且您需要在字段前面加上表名或别名,以防止引用不明确的列(例如,具有不同表中的相同名称)

Since, you're looking to improve the code, I would make a few suggestions.由于您希望改进代码,因此我会提出一些建议。

As @Strawberry suggests, the procedure is not necessary if the end goal is just to query for a reservation.正如@Strawberry 建议的那样,如果最终目标只是查询预订,则不需要该过程。 While it may be possible that you intend to expand the functionality of the procedure, it's generally better IMHO to avoid them for simple calculations, aggregations, etc. that could be done in plain SQL.虽然您可能打算扩展该过程的功能,但恕我直言,通常最好避免它们进行简单的计算、聚合等,这些可以在普通的 SQL 中完成。

Concatenating strings together to form SQL, while using user input, can be dangerous.在使用用户输入时,将字符串连接在一起形成 SQL 可能很危险。 It's safe in this case, since keyboard.nextInt() would prevent a SQL injection attack, but you might consider using prepared statements to get additional level of protection, and prepared statements can have other advantages as well.在这种情况下是安全的,因为keyboard.nextInt()可以防止 SQL 注入攻击,但是您可以考虑使用准备好的语句来获得额外的保护级别,准备好的语句也可以具有其他优势。 For example, some databases will cache the runplan, which can be significant when a more complex query will be run a number of times.例如,一些数据库会缓存运行计划,这在更复杂的查询将运行多次时可能很重要。

You may always want to user an "on" clause in your joins.您可能总是希望在联接中使用“on”子句。 Otherwise you will get a cross join, which may not be what you want.否则你会得到一个交叉连接,这可能不是你想要的。

Once you get beyond simple examples, embedding SQL in Java code can become tedious, and externalizing it as a resource can be very useful, and sets up for templating the SQL, or having variations to deal with quirks between different databases (eg MySQL vs Oracle vs Postgres, etc.). Once you get beyond simple examples, embedding SQL in Java code can become tedious, and externalizing it as a resource can be very useful, and sets up for templating the SQL, or having variations to deal with quirks between different databases (eg MySQL vs Oracle与 Postgres 等)。

There are a whole bunch of opinions out there about things like natural keys vs surrogate keys, or lowerCamel columns vs lower snake case columns, when to use or avoid stored procedures, etc., but rather than attempt an exhaustive "best practices" list,which would just be my opinion anyway, here's an alternate example that's not too far from your original example (ie ignoring ORM's, etc)关于自然键与代理键、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"))
                );
    }
}

Run it like this (don't use passwords on the command line in real life)...像这样运行它(在现实生活中不要在命令行上使用密码)...

java Hotel <url> <user> <pwd>

The output should look like this for guest 1...对于访客 1,output 应该如下所示...

(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

and this for guest 2...这对于客人 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 

Here's the externalized SQL...这是外化的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 = ?;

Here's a full MySQL schema if you want to try it out...如果您想尝试一下,这里有一个完整的 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 ;

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

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