回答

收藏

Mysql / PDO违反完整性的主数据库重复-尽管表中没有任何内容

技术问答 技术问答 80 人阅读 | 0 人回复 | 2023-09-13

我已经看过SQL主键约束,尽管不存在记录,所以SO上所有问题中的哪个似乎最接近我的问题,但并不相同。
, a4 P/ S. Z6 S- A9 j4 h7 U毫无疑问,我可能正在做一些愚蠢的事情,但是这里有:
- w% j3 j& R1 a0 W% V' e" G7 n我正在尝试编写一个脚本(用php编写),该脚本将从任何给定的PDO数据库迁移数据(没有结构,它假定结构已经完成)到任何其他给定的PDO数据库-
6 x6 t7 F# r3 h  C3 H4 t在我的情况下,我正在sqlite3上对其进行测试- > MySQL。
+ E8 F3 }0 d2 e: Z- Z当我在测试数据库上运行脚本时,我收到“完整性约束冲突:1062项“ PRIMARY”的重复项“
6 H' R0 s( K0 F1””,我不太了解,因为表中没有数据(即使在脚本运行之前也是如此) DELETE语句)。& P  a2 i$ s4 ?2 a  p. }
我假设这与主键是auto_increment有关,但是我尝试将下一个增量值设置为除要插入的任何值之外的其他值(认为我尝试将其设置为80)-没什么区别。0 T+ r- I/ v/ @5 {9 E* N
我一直在寻找一种在交易过程中禁用auto_increment的方法,但没有事先更改表,然后再更改回去的方法,我想不出办法-
: d7 j4 L, ~- _9 m& u- q" {) ]更改整个表似乎是错误的,我并不想参与任何DDL。
0 B4 Q# p+ [" P- V/ t- p# Y0 g  1 setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
" y3 c; e/ H  K0 Q: Z; |' X" a2 m8 @ 201 R% x  I" i  b2 i% `  O
21         // connect to target
9 T- D( V' c/ c 22         $target = new PDO($target_dsn, $target_username, $target_password);( V2 a. K+ V7 c  J1 [6 M
23         $target->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
% B  K( d8 V/ q/ { 24
0 P, S4 ]0 g- e" F; k 25         //TODO Generalise this statement to all database types.2 u$ t/ i! k! r- F- ?/ I5 C
26         $stmt = $source->prepare("SELECT * FROM sqlite_master WHERE type='table';");
- x0 ^: ?9 U) w' I) R$ h 27         $stmt->execute();7 X: J) k3 d5 N8 D
28
8 H( L; E" l* `0 S' F 29         // get all tables
) t$ y: d4 T  E" v3 x 30         while($tablerow = $stmt->fetch(PDO::FETCH_ASSOC))
) E  B. z8 x! H. D- j" t3 a 31         {( ]. C& [6 `( M/ ~3 m) P
32                 // TODO Generalise these statements to all database types.
" E, J/ Y4 B' o3 u, H 33                 $transfer_data[$tablerow['tbl_name']] = array();
: x( A0 \( {2 {2 l9 v" f- D4 { 34                 $table_data[$tablerow['tbl_name']] = array();
1 I6 _( |# K* y! V 35         }% T! E3 F* p4 N0 J& g8 V
36         $stmt->closeCursor();, [: U2 k! F9 @7 N0 `- N- m9 @
37
& V1 d6 P% ]% O, h; C- t& j 38         // for each table, load data
# y# b; c) C" c1 G$ j 39         foreach($transfer_data as $tablename => $void)9 p2 G! _) s3 x: m+ b' t4 M
40         {
% K; T2 h+ h: G8 N: f  q( M 41                 $stmt = $source->prepare("SELECT * FROM $tablename;");
  S8 y6 E5 g- ~8 v( ^+ d 42                 $stmt->execute();
% ?4 p( v* M9 E$ f% R! p 43                 // load data row at a time/ s) C5 c0 a6 ?' b; K. d1 ~
44                 while($datarow = $stmt->fetch(PDO::FETCH_ASSOC))# i8 P7 N4 E1 x) b# U
45                 {7 D+ G/ p2 h3 ?0 k
46                         // store data for later
4 y& `! V0 C. S) W/ p" G' Q 47                         $transfer_data[$tablename][] = $datarow;2 X- s$ I. @6 l
48                         // if we haven't gained column data yet, do so now, P, w* q+ g  x9 X" a) E
49                         if(!array_key_exists($tablename,$table_data))
8 r" @7 Q' O. T, k, J 50                         {
- `. p% i3 A* ^4 C& ` 51                                 $t_data = array();: t- w3 i1 |: L) o/ f
52                                 foreach($datarow as $colname => $void)
9 R) c* S- r7 A- u% a, o( r. b 53                                 {7 ]) m; t% M3 ~' X: k% Q/ i
54                                         $t_data[] = $colname;
6 m( b+ v  J; H4 i; d9 ]: @ 55                                 }7 |$ s) |' ^+ ^7 }' r
56                                 $table_data[$tablename] = $t_data;+ g' Y' l/ E( z, W
57                         }
% a" r- b" G7 j" k 58                 }4 Y4 v6 ~. X2 @/ r. M1 ^7 p
59                 $stmt->closeCursor();. Q" O  V1 j$ [! Y  f% i/ o
60                 echo "Read $tablename\n";" c5 l( H, h' w
61         }9 n9 b! T" i8 U' {1 ~, ^
626 s( M) C; d0 i6 M2 V
63         //start a transaction (if driver supports transactions / if not then this is noop)" X* n+ J$ O; K1 b& o" b8 e5 n
64         $target->beginTransaction();
( d+ G; J* W* i5 ?& i& c& \9 i  x 65         // for each table clear existing data and insert copied data+ u9 e' o7 T. `, y& R
66         foreach($table_data as $tablename => $columns)
3 p) f1 M" z& g  i$ D8 h 67         {4 |' _1 F7 Q; C" Z5 {( o
68                 // not using an empty/truncate because mysql and possibly others autocommit
4 \; E$ ?9 d' ~ 69                 $stmt = $target->prepare("DELETE FROM $tablename;");: x# N  Q# m1 L5 D- \
70                 $stmt->execute();
. l. a6 Q! N6 x! q6 N" B! J) L 71                 $stmt->closeCursor();' ^$ n. S0 F4 L. {: G
72
4 v, u" C1 W$ n0 Y! q2 F 73                 // prepare the insert statement - we don't know how many columns so is dynamic, y: ~" E& M' `- H
74                 $querystr = "INSERT INTO $tablename (".join(", ",$columns).") VALUES (";
5 Z1 I3 O6 [5 M/ U" m% q5 M 75                 foreach($columns as $k => $column); z7 U9 `6 M! f8 C% M" b
76                 {, J) q, N1 K: l( Q( W- p
77                         $columns[$k] = ':'.$column;
1 b% z. C; Z! I9 T0 \ 78                 }
& A4 Q% E) _; J8 s8 K0 a 79                 // using named placeholders so order doesn't matter
  Y7 R, ?  ^$ E3 }% ~ 80                 $querystr = $querystr.join(", ",$columns).");";1 |5 {: ~) H( i, }
81                 $stmt = $target->prepare($querystr);) u  S* u. A# j; j6 T' [% Z' j
82                 //echo "Using: $querystr\n";
5 {. k+ K7 h4 y% K 83                 $rowcount = 0;
. C& X, l' ?, w; m5 `5 \" b 84                 // for each row of data, bind data and execute insert statement
& T! F. r, O7 T3 t) y) C( x# L 85                 foreach($transfer_data[$tablename] as $rowdata). [) l4 p; ~% U, w) G" n  B
86                 {
, S5 H* o, Z  e. `& I# \ 87                         foreach($rowdata as $rowname => $rowvalue). S0 w8 e1 p, @# }
88                         {. X3 m* [! R+ H$ l; @2 q2 |4 g" }2 F  m
89                                 $stmt->bindParam(':'.$rowname, $rowvalue);( l1 x# ~$ r- p) C  ?
90                         }1 D7 _. J) o+ R. u7 o/ F: \$ O
91                         $stmt->execute();
. a3 h: L3 y- k, h3 |% Y" M 92                         $stmt->closeCursor();
; s7 q# ?) Y! N2 E* Q) w& U 93                         $rowcount++;
1 k* ?0 b3 z- n- H4 z2 c 94                 }, R6 |) v5 {8 L, c4 o0 l' D
95                 echo "Written $rowcount rows to $tablename\n";
. A+ F. L% l( x3 M; Y4 H, u" v& a 96         }
. U, s2 n1 ?5 E% \% U( v 97         $target->commit();
* v/ `2 H2 k+ I7 e* k 98! C" c/ v# g1 e0 F' E
99 }9 K% r4 h) @( V' n: a
100 catch (PDOException $e)
' A. i6 b5 i4 m9 w: {5 @* ^101 {
% L) B$ O5 w+ c( n3 ?$ @4 J102         echo 'PDO Error: '.get_class($e).' - '.$e->getMessage()."\n";5 t* j2 b! o6 v3 Y, b
103         echo 'Query String was: '.$querystr."\nData:\n";
( r) d$ _) v- [+ P) c, ^104         var_export($transfer_data[$tablename]);
: ]( E$ D1 [, B' o2 e8 H105         if($target->inTransaction()){6 u; L0 G. V  ~! J' H& n  X
106                 $target->rollBack();
' B0 f5 P/ u% H( F7 O( Z3 [3 v! A107         }
) J7 U2 }  D$ c/ u2 X! p108 }  c* C0 q. g: U/ R$ |2 m
109
: q9 s. ?0 ]: h; [) u9 C. p2 m现在我的目标数据库中有一个表,该表是:
# f$ d8 q5 z6 l, n8 U& T+-------------------+------------------+------+-----+---------+----------------+* j5 Z. f9 D6 H7 k+ o, a: ]
| Field             | Type             | Null | Key | Default | Extra          |/ A1 n& d$ D! I+ U" I
+-------------------+------------------+------+-----+---------+----------------+2 _# O( j  j7 e' Z9 P) D6 \/ ~
| channel_id        | int(11)          | NO   | PRI | NULL    | auto_increment |5 B, D, f2 c6 S1 A  W: O( }
| channel_parent_id | int(10) unsigned | YES  |     | NULL    |                |# V& {  `, m5 h+ C( ?3 S
| server_id         | int(10) unsigned | NO   | MUL | NULL    |                |# N! v- b  h) s1 `
+-------------------+------------------+------+-----+---------+----------------+8 O5 E. ]5 {/ A
输出为:7 F5 ?, |/ D4 C
PDO Error: PDOException - SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1' for key 'PRIMARY'
2 L  `$ Y/ G2 ?! f/ tQuery String was: INSERT INTO channels (channel_id, channel_parent_id, server_id) VALUES (:channel_id, :channel_parent_id, :server_id);( F* W* F2 a" k
Data:
! q  E; Y- S6 R9 F  i9 Y8 e; Sarray (
" w* `: i- }; f+ ^  0 =>% l( i+ Y9 U$ j& N- J5 s
  array (# N2 s$ b2 U  y
    'channel_id' => '1',$ G# R. Q# v) P5 g! j7 ~2 `" K
    'channel_parent_id' => '0',) m/ K- G- c# s3 ~1 f' F
    'server_id' => '1',
  C- Z9 M9 u0 h  ),, k" W0 I( e5 J' }8 M
  1 =>
3 a% O( [% |% C4 q2 T  array (
$ M1 g) s* J3 _3 e' k( g) ^    'channel_id' => '24',5 O* W5 f7 U4 ]' ]' j
    'channel_parent_id' => '0'," F# \) e3 i  D. n8 o+ J
    'server_id' => '1',% `6 e/ g0 N0 O0 Q% v* \- h
  ),
$ V, N' e% G5 @7 z7 m8 x. @9 P! r  2 =>
  y% \' M- P: }0 m: r. F0 S* M# ~# w: ~  array (
% w: u" B" D# K' K+ g' X    'channel_id' => '34',# L  _6 e& Y* E1 n0 @: ?
    'channel_parent_id' => '0',' g& W0 i7 G, W: A* ]' v
    'server_id' => '1',9 Q: j( x, ~; n0 }) h* G
  ),9 o3 @# p" _" ^2 }) @3 \9 S
               
# e3 o4 }4 s2 J+ {7 }; F: n/ f解决方案:, D$ P$ l1 b: H7 p; Q' s* ]  x! a: h. r
                & y; K$ H: o. G, _1 ~0 }: Z
# G' Z% r% q. M& _" f' s8 j1 Y

% F, ?* ]5 q4 ]                很高兴您解决了这个问题。然而,这是为了应对 理由 为什么bindParam()不为你工作。这不是错误,它是通过设计以这种方式工作的。7 ?$ e+ ~6 d( W0 @; N2 [4 N
根据文档:  n% A$ f  T- U* y5 A6 C

  j* _9 G! l. N' [% U& U将PHP变量绑定到用于准备语句的SQL语句中的相应命名或问号占位符。与PDOStatement :: bindValue()不同,该变量被绑定
# T. j9 ~5 x5 O2 b* ]为引用,并且仅在调用PDOStatement :: execute()时进行评估" S9 |/ x7 [6 F: v
4 E- }7 H9 q. @3 `! m
(强调我的)0 d' V& ]% l! w) E7 M& ^
考虑到上述情况,这是:
/ i% b# i4 B  T+ H5 B: z6 S/ N 87                         foreach($rowdata as $rowname => $rowvalue)8 p( O0 A# J- i) b' f
88                         {" m! u& O! D/ a+ ]# s  V/ A5 h
89                                 $stmt->bindParam(':'.$rowname, $rowvalue);- a( |& a( D5 T
90                         }
9 h8 S/ S+ `% A8 q…将$rowvalue 通过 查询将每个参数绑定到 引用 ,在执行查询时,该 引用 将始终是1(的最后一个元素$rowdata)1 L' N  r! r4 V( h
使用它的方式bindParam()可能是这样的:( J* p  l' p, N3 S( Y1 B: M
87                         foreach($rowdata as $rowname => $rowvalue)
3 g- ^7 l* s/ L2 l% H+ @: D 88                         {% Y4 W- j- k/ R0 g3 J" y8 @4 d
89                                 $stmt->bindParam(':'.$rowname, $rowdata[$rowname]);
, W: t; o1 d" O' ^5 M 90                         }6 `, C! l) b5 \( {4 G- c/ Q2 k. Y
…或者,甚至:
4 t$ A- V+ a& I) y2 t& a 87                         foreach($rowdata as $rowname => &$rowvalue)3 ?* r( |! _/ H5 s3 P
88                         {+ Y0 x8 s) t! ^- B- b
89                                 $stmt->bindParam(':'.$rowname, $rowvalue);" F  `- q+ ~4 a8 {5 k# b* T
90                         }
" w4 Y1 C+ }  B" [. B…,以便每个参数都引用相应的array 元素 。" ^3 L6 f0 E! x
如上所述,一种替代方法是 _通过值_而不是通过引用bindValue()绑定参数。这意味着将在调用该参数时对其进行评估,而不是在实际需要该参数时(即查询执行)进行评估:__bindValue()% A) e3 T" }6 I* |& t
87                         foreach($rowdata as $rowname => $rowvalue)
, P. p! R! V  _! o/ I 88                         {3 @! H4 U6 a8 b; x8 @
89                                 $stmt->bindValue(':'.$rowname, $rowvalue);
% @# Z: B* s5 |9 b1 n& @: {8 z" M6 Z 90                         }
; l, _5 S4 e5 `4 L当然,另一种选择是输入execute()一组参数,这可以execute()解析绑定部分 (因此是我个人的最爱!) 。
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则