MySQL без первичного ключа - PullRequest
0 голосов
/ 19 марта 2011

у меня есть этот код здесь

drop table if exists Payments;

create table Payments
(
    customer_email VARCHAR(50) NOT NULL,
    amount DOUBLE,
    payment_type ENUM('Visa','Mastercard', 'Cash'),

    PRIMARY KEY (customer_email),
    FOREIGN KEY (customer_email) references customer(email)

);

теперь каждый раз, когда я вписываю платеж покупателя покупателем, вводя его адрес электронной почты и сумму.Скорее всего, каждый раз, когда я вхожу в одно и то же письмо, я получаю ошибку первичного ключа (не могу продублировать первичный ключ)

первичный ключ здесь относится к таблице, которая состоит из личных данных клиента.

есть идеи?

Ответы [ 3 ]

7 голосов
/ 19 марта 2011

Первичный ключ должен быть уникальным - если у каждого клиента будет несколько платежей, нельзя указывать customer_email в качестве первичного ключа. Попробуйте добавить отдельный столбец идентификатора и сделать его первичным ключом.

2 голосов
/ 19 марта 2011

Я бы нормализовал ваш дизайн и использовал бы хранимую процедуру для вставки платежей следующим образом:

Полный скрипт здесь: http://pastie.org/1688269

Надеюсь, это поможет:)

Пример звонков

call insert_payment('foo@bar.com',1,100);

call insert_payment('bar@foo.com',2,200);

call insert_payment('pants@elis.com',3,300);

call insert_payment('another@customer.com',1,400);

call insert_payment('another@customer.com',2,500);

mysql> select * from payments_view order by pay_id desc;
+--------+---------------------+-------------+---------------+--------+---------+----------------------+
| pay_id | pay_date            | pay_type_id | pay_type_name | amount | cust_id| email                |
+--------+---------------------+-------------+---------------+--------+---------+----------------------+
|      5 | 2011-03-19 01:34:28 |           2 | mastercard    | 500.00 |       4| another@customer.com |
|      4 | 2011-03-19 01:34:28 |           1 | visa          | 400.00 |       4| another@customer.com |
|      3 | 2011-03-19 01:34:28 |           3 | cash          | 300.00 |       3| pants@elis.com       |
|      2 | 2011-03-19 01:34:28 |           2 | mastercard    | 200.00 |       2| bar@foo.com          |
|      1 | 2011-03-19 01:34:28 |           1 | visa          | 100.00 |       1| foo@bar.com          |
+--------+---------------------+-------------+---------------+--------+---------+----------------------+
5 rows in set (0.00 sec)

Хранимая процедура

Хранимая процедура сначала проверяет, существует ли учетная запись клиента, если нет, то создает ее, а затем вставляет данные платежа.

delimiter ;

drop procedure if exists insert_payment;

delimiter #

create procedure insert_payment
(
in p_email varchar(512),
in p_pay_type_id tinyint unsigned,
in p_amount decimal(10,2)
)
begin

declare v_cust_id int unsigned default 0;

  if not exists (select 1 from customers where email = p_email) then
    insert into customers (email) values (p_email);
    set v_cust_id = last_insert_id();
  else
    select cust_id into v_cust_id from customers where email = p_email;
  end if;

  insert into payments (cust_id, pay_type_id, amount) 
   values (v_cust_id, p_pay_type_id, p_amount);

  select last_insert_id() as new_pay_id;

end#

Таблицы, представления и триггеры

drop table if exists payments;
drop table if exists payment_types;
drop table if exists customers;

create table payment_types
(
pay_type_id tinyint unsigned not null auto_increment primary key,
name varchar(255) unique not null
)
engine=innodb;

create table customers
(
cust_id int unsigned not null auto_increment primary key,
email varchar(512) unique not null,
total_amount_paid decimal(10,2) not null default 0
)
engine=innodb;

create table payments
(
pay_id int unsigned not null auto_increment primary key,
cust_id int unsigned not null,
pay_type_id tinyint unsigned not null,
pay_date datetime not null,
amount decimal(10,2) not null default 0,
key (pay_date),
foreign key (cust_id) references customers(cust_id),
foreign key (pay_type_id) references payment_types(pay_type_id)
)
engine=innodb;

drop view if exists payments_view;
create view payments_view as 
select
 p.pay_id,
 p.pay_date,
 p.pay_type_id,
 pt.name as pay_type_name,
 p.amount,
 c.cust_id, 
 c.email
from
 customers c
inner join payments p on c.cust_id = p.cust_id
inner join payment_types pt on p.pay_type_id = pt.pay_type_id;

delimiter #

create trigger payments_before_ins_trig before insert on payments
for each row
begin
 set new.pay_date = now();

 update customers set total_amount_paid = total_amount_paid + new.amount
  where cust_id = new.cust_id;
end#

delimiter ;

Тестирование

insert into payment_types (name) values ('visa'),('mastercard'),('cash');

insert into customers (email) values ('foo@bar.com'),('bar@foo.com'),('pants@elis.com');

call insert_payment('foo@bar.com',1,100);
call insert_payment('bar@foo.com',2,200);
call insert_payment('pants@elis.com',3,300);
call insert_payment('another@customer.com',1,400);
call insert_payment('another@customer.com',2,500);

select * from payment_types order by pay_type_id;
select * from customers order by cust_id;
select * from payments order by pay_id;
select * from payments_view order by pay_id desc;
0 голосов
/ 19 марта 2011

Значение первичного ключа может существовать только один раз в столбце. Чтобы поддержать значение, существующее более одного раза, вы либо:

  • не может наложить ограничение первичного ключа (или уникального в этом отношении) на столбец
  • использовать более одного столбца в качестве первичного ключа (называемого составным ключом)

Я бы решил вашу проблему, добавив дату, когда кто-то сделает платеж:

CREATE TABLE Payments (
  customer_email VARCHAR(50) NOT NULL,
  payment_date DATETIME,
  amount DOUBLE,
  payment_type ENUM('Visa','Mastercard', 'Cash'),
  PRIMARY KEY (customer_email, payment_date),
  FOREIGN KEY (customer_email) references customer(email)
);

Дата имеет смысл, потому что это то, что кому-то может понадобиться / использовать для составления отчетов. Поскольку дата и время сохраняются, очень маловероятно, что у вас будут дубликаты одинаковых значений даты (что приведет к ошибке, такой как та, с которой вы уже столкнулись). Также легко заполнить значение даты в операторе INSERT, используя NOW() или стандарт ANSI CURRENT_TIMESTAMP ... или вы можете определить ограничение DEFAULT для столбца, чтобы автоматически использовать текущую дату при вставке данных.

...