回答

收藏

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

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

我已经看过SQL主键约束,尽管不存在记录,所以SO上所有问题中的哪个似乎最接近我的问题,但并不相同。
( q% {6 }4 u4 d' s+ N' z" B$ B毫无疑问,我可能正在做一些愚蠢的事情,但是这里有:# }6 [: |4 R( ^! \: o9 p
我正在尝试编写一个脚本(用php编写),该脚本将从任何给定的PDO数据库迁移数据(没有结构,它假定结构已经完成)到任何其他给定的PDO数据库-
! T( \5 B. N" {8 C$ B& [% X, \在我的情况下,我正在sqlite3上对其进行测试- > MySQL。( j  ~; J9 l/ ^0 S/ G/ [
当我在测试数据库上运行脚本时,我收到“完整性约束冲突:1062项“ PRIMARY”的重复项“8 G% ^9 `3 K* f8 V! x- z
1””,我不太了解,因为表中没有数据(即使在脚本运行之前也是如此) DELETE语句)。, H" g: e! Z# q  z
我假设这与主键是auto_increment有关,但是我尝试将下一个增量值设置为除要插入的任何值之外的其他值(认为我尝试将其设置为80)-没什么区别。4 O! l: N' P- h& a* ^$ a
我一直在寻找一种在交易过程中禁用auto_increment的方法,但没有事先更改表,然后再更改回去的方法,我想不出办法-/ |) j0 A1 C8 i) l3 |
更改整个表似乎是错误的,我并不想参与任何DDL。& B) q; o* F- g" U+ y0 y# N
  1 setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);" O  }: Y; @) B0 w. M* G
20
9 ^$ Y9 A" d% r: ?+ S; t9 L 21         // connect to target4 T: H* t' p, f8 p
22         $target = new PDO($target_dsn, $target_username, $target_password);% X, @5 v2 }5 j. s3 G
23         $target->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);5 u: D' y" e) f- L9 D3 o! |
24) Y  {! b. W8 h; E
25         //TODO Generalise this statement to all database types.
5 j- [/ M3 E  c5 W 26         $stmt = $source->prepare("SELECT * FROM sqlite_master WHERE type='table';");
! T: J  l2 i# o" P: W/ P4 c 27         $stmt->execute();
  S: Q4 n- v" c1 E6 k 28% {% r6 i( B8 W$ p2 d# q
29         // get all tables0 Q1 A0 @2 V0 [" n' u% V- x  }
30         while($tablerow = $stmt->fetch(PDO::FETCH_ASSOC))
. R7 o4 e# H/ U2 T& {- j 31         {
* v) H& E( L( r! u9 w9 S 32                 // TODO Generalise these statements to all database types.. W3 ~" U7 V" q$ ]4 Y1 M
33                 $transfer_data[$tablerow['tbl_name']] = array();9 q& e7 R9 i/ X' i/ o8 ?
34                 $table_data[$tablerow['tbl_name']] = array();
* T, W8 x% O. m! l9 } 35         }! V& E  |+ x: O7 c6 y( A
36         $stmt->closeCursor();
7 ~' q( `6 z: u4 M( D 377 E- d% ]5 k& s) `
38         // for each table, load data# I* P& I& L8 k& z. m3 e* T
39         foreach($transfer_data as $tablename => $void)' X% {: ]1 r6 V5 ^
40         {
* D) a, Q& x& r6 Q; z# e+ G7 I  ^ 41                 $stmt = $source->prepare("SELECT * FROM $tablename;");8 Y" j& p) u  r5 |! K% L6 P/ R
42                 $stmt->execute();5 V) L3 q0 s7 O7 f/ X: c
43                 // load data row at a time
' T0 E3 G  [) h& F 44                 while($datarow = $stmt->fetch(PDO::FETCH_ASSOC))* v2 b1 Y7 W0 I
45                 {/ S( A8 `! O$ Z
46                         // store data for later
  z- g% M- F# j+ }; V) f7 W 47                         $transfer_data[$tablename][] = $datarow;3 k3 R, l$ l3 r. P
48                         // if we haven't gained column data yet, do so now+ x" c. D7 }& K* q% x
49                         if(!array_key_exists($tablename,$table_data))3 a; n( D+ ^( Q, S! m! \
50                         {
% B- b) @) U3 D4 t0 v 51                                 $t_data = array();, C) W9 F4 |$ l$ x/ Y- `
52                                 foreach($datarow as $colname => $void)1 u" g3 ]5 g8 J
53                                 {, l# E, ?9 g' h( M
54                                         $t_data[] = $colname;$ P* }' x7 W3 _* B2 r3 ?
55                                 }
. J; u% M/ w7 u& C1 i2 t 56                                 $table_data[$tablename] = $t_data;
- \5 f8 V8 ^( K, F) C+ z 57                         }
+ X7 ]5 T: U8 i3 }0 ]6 k" l9 { 58                 }
+ M+ h2 ]  o8 N6 J+ E 59                 $stmt->closeCursor();
7 d" U0 ?) K5 d5 N- U# o 60                 echo "Read $tablename\n";
( l, L5 i9 t  P 61         }
' d- w. u8 X. Z 620 _- m- @. d0 V$ z" G/ V
63         //start a transaction (if driver supports transactions / if not then this is noop)" B# g2 b& X% b6 O
64         $target->beginTransaction();
# F" f$ W9 s) D  \ 65         // for each table clear existing data and insert copied data1 ~9 h) F. y: j0 I- @, ?6 I* u+ B  U
66         foreach($table_data as $tablename => $columns)
/ `$ A# S; i) ] 67         {
* f. X5 p5 u1 r% r) Z% ^$ V 68                 // not using an empty/truncate because mysql and possibly others autocommit9 ~+ ]" x+ `$ e: {$ M$ {* P
69                 $stmt = $target->prepare("DELETE FROM $tablename;");
% Z% R' u! P8 P 70                 $stmt->execute();
+ Y! N  e' {8 `6 K1 m 71                 $stmt->closeCursor();
9 ]) u6 P+ y% ]5 u( F 728 Y! }# M( M5 [# y$ x, j: y
73                 // prepare the insert statement - we don't know how many columns so is dynamic
& p9 D3 J. [# P' M5 O+ } 74                 $querystr = "INSERT INTO $tablename (".join(", ",$columns).") VALUES (";
! ^; f1 {9 Q, j& R+ h! i4 e. n 75                 foreach($columns as $k => $column)3 t6 g+ @8 [; s
76                 {
* }/ O' x3 b' Q) _  T4 E& f6 {4 W6 ~& B 77                         $columns[$k] = ':'.$column;0 M2 v2 m6 j/ h/ h  K& ?; w
78                 }
4 e3 t  V5 e0 D) V. G: } 79                 // using named placeholders so order doesn't matter
2 H; J$ z2 e4 A5 w+ j  ]$ q 80                 $querystr = $querystr.join(", ",$columns).");";/ S$ l' o2 M9 A7 T
81                 $stmt = $target->prepare($querystr);
$ s" F- |% D7 X  ?1 a2 Z# t/ i: ^- E 82                 //echo "Using: $querystr\n";3 o! s3 R) @) J9 I; k$ y6 |
83                 $rowcount = 0;
. C. \' g3 `4 I5 b. U) z 84                 // for each row of data, bind data and execute insert statement3 L1 U' }3 O8 w/ C  d+ e, S3 o! z
85                 foreach($transfer_data[$tablename] as $rowdata)
5 N5 d8 E/ c  n3 D 86                 {: y. v- d" W; x( X1 J7 S
87                         foreach($rowdata as $rowname => $rowvalue)8 Z2 B) X. H+ v/ o% D( v
88                         {
* {# Z$ K5 t8 p: H& x 89                                 $stmt->bindParam(':'.$rowname, $rowvalue);
- t3 q' D$ p0 X  s2 U# S4 @6 K 90                         }
7 c% }  F% \* o9 @3 K 91                         $stmt->execute();
1 F- J$ _! I  h0 r: S" z7 V 92                         $stmt->closeCursor();' p: A3 ]$ k- l/ N$ e. @% s8 E  N
93                         $rowcount++;8 D* L1 \; N% r
94                 }% B5 y6 `4 y$ _: ~/ j3 E
95                 echo "Written $rowcount rows to $tablename\n";5 P0 h, ~, O5 ~* J7 [
96         }
+ h1 P2 o" M4 o) I) m# ]& I 97         $target->commit();
; N' E  G3 v. Q% U( e3 x 98# u2 V# U+ b& a( c5 n# C+ u9 r
99 }. N/ m& ?' K3 r' _4 i) M3 l
100 catch (PDOException $e)
0 _7 N* m' _7 Q7 A: ^. D2 E101 {
# w) f$ `$ M" M. a2 f102         echo 'PDO Error: '.get_class($e).' - '.$e->getMessage()."\n";) u' r+ ]. y6 N9 r2 d+ v
103         echo 'Query String was: '.$querystr."\nData:\n";
  o; P1 C+ q5 o5 a. C' R. L' M: ]104         var_export($transfer_data[$tablename]);6 t  E% q/ Y: H; {
105         if($target->inTransaction()){" m, r. |: |- r1 W4 C
106                 $target->rollBack();) v6 o; m, C4 O% [' W+ |  M. {
107         }
( }6 s+ v8 h- E. J% D4 B6 a6 g$ z* S108 }0 I$ J) I4 Z/ o* @9 `2 D6 ]
109( F* {4 m, h, D6 L" J
现在我的目标数据库中有一个表,该表是:+ f" G1 I& A) I0 w
+-------------------+------------------+------+-----+---------+----------------+) i8 e/ D. x  U5 _4 N6 o
| Field             | Type             | Null | Key | Default | Extra          |
* l* L9 b' h& Y+ s9 }" A  L5 R+-------------------+------------------+------+-----+---------+----------------+  |% |; {4 O9 ?! J" N
| channel_id        | int(11)          | NO   | PRI | NULL    | auto_increment |9 Z! y2 M2 S# D! e( U: F
| channel_parent_id | int(10) unsigned | YES  |     | NULL    |                |
0 X5 @0 e7 j9 y2 o1 \| server_id         | int(10) unsigned | NO   | MUL | NULL    |                |
! T9 ^( s  F0 b8 k9 f) u* K& [% C+-------------------+------------------+------+-----+---------+----------------+
1 x9 l  W  \: s. j/ J& ]输出为:
+ B: M) }" G* y( q0 g' I  YPDO Error: PDOException - SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1' for key 'PRIMARY'
+ M$ s0 |  @  JQuery String was: INSERT INTO channels (channel_id, channel_parent_id, server_id) VALUES (:channel_id, :channel_parent_id, :server_id);
- L' Q2 E0 Z" X& O) {5 g! aData:
5 `+ F3 o: M2 K( z2 n: Sarray (3 i( b1 r. R/ M' a2 |/ T/ z: R
  0 =>
0 @5 [0 B5 h9 p' M) k  array (1 K3 b: e5 s9 `$ x* h: K
    'channel_id' => '1',
  }2 k4 X6 R8 [3 N, n    'channel_parent_id' => '0',( [+ Z- D) o$ y2 m1 P0 m
    'server_id' => '1',. l- [$ M8 J1 r# N# |
  ),
4 m5 }6 D1 h6 w, i  1 =>5 A7 T, Z1 ~5 o2 D9 o7 W  X: ?
  array (; t4 s7 F) Y/ Z2 @
    'channel_id' => '24',4 u1 ~/ Y! i9 \6 _: |
    'channel_parent_id' => '0',$ a8 Z% J( \  E4 ?
    'server_id' => '1',
  d/ w, b7 f" j  K5 h. @# G8 Z; s4 d  ),
! T  ~) C4 H/ T: g  l6 N  2 =># {9 x' y) _% @! _8 v
  array (
, p' X) ^% \3 m  H    'channel_id' => '34',
6 n$ i+ Q4 N- K! Z$ e    'channel_parent_id' => '0',
% ]+ o  c+ D# |9 w* m    'server_id' => '1',& P  I+ V0 \4 Y
  ),
, T" o8 Q, h2 D# T+ |; O                / N: K, X, G* C' _3 f
解决方案:0 Q, v8 _0 O  Q2 S- O+ d  x* O
               
$ x4 }5 M- |7 `" L% R/ ]" s$ u2 {2 x1 K
6 r' h6 o9 ]8 y) I) a" U$ I
                很高兴您解决了这个问题。然而,这是为了应对 理由 为什么bindParam()不为你工作。这不是错误,它是通过设计以这种方式工作的。
! i! h# D3 ~, y) l% f: r根据文档:
" i$ d6 a: M- B: @$ Y% K# |
9 @" X7 t5 H  G7 Y; |将PHP变量绑定到用于准备语句的SQL语句中的相应命名或问号占位符。与PDOStatement :: bindValue()不同,该变量被绑定' O* }3 n! y5 Y2 d
为引用,并且仅在调用PDOStatement :: execute()时进行评估5 A8 r) }1 `* c) r* V: ]
2 c) {1 F7 ^& M, `7 u/ ?
(强调我的)
( ], z/ Q4 |$ o; k- O考虑到上述情况,这是:  {- r( T- x+ d8 C$ d
87                         foreach($rowdata as $rowname => $rowvalue)9 B( d; E3 x4 U! @6 y/ f
88                         {
: @' D3 y! h- M- s6 K7 |# I6 K 89                                 $stmt->bindParam(':'.$rowname, $rowvalue);1 C9 ]) W4 F' _
90                         }7 t3 }# d- R) a3 x1 I+ ]1 Q6 q" j( c
…将$rowvalue 通过 查询将每个参数绑定到 引用 ,在执行查询时,该 引用 将始终是1(的最后一个元素$rowdata)( g9 f$ Z& }9 |9 M$ o& w
使用它的方式bindParam()可能是这样的:
1 K4 k6 N$ G1 l 87                         foreach($rowdata as $rowname => $rowvalue); `4 z& m- |1 Q8 s
88                         {5 q" q2 [- F2 Z% c5 f+ ]# |' n
89                                 $stmt->bindParam(':'.$rowname, $rowdata[$rowname]);, K# ^+ ]- D0 a8 [
90                         }( {8 ]( P4 L) p9 W- M% K; O" g3 e# ^$ M
…或者,甚至:
& [7 N" a9 H1 G, o# @6 |( }2 A 87                         foreach($rowdata as $rowname => &$rowvalue)# }- D- s7 A9 x1 V2 S5 p2 A* N1 I8 k1 c
88                         {
( ^' I' H( \% k% C 89                                 $stmt->bindParam(':'.$rowname, $rowvalue);
$ I; R* A- ~, J1 N5 T 90                         }
* _8 Y9 v. L/ j5 [( M…,以便每个参数都引用相应的array 元素 。
3 a$ z6 A; B3 }2 ]: R7 U* L, N4 R如上所述,一种替代方法是 _通过值_而不是通过引用bindValue()绑定参数。这意味着将在调用该参数时对其进行评估,而不是在实际需要该参数时(即查询执行)进行评估:__bindValue()
  X6 t; u5 Z. J  a  W) A% r  | 87                         foreach($rowdata as $rowname => $rowvalue)) E; c6 Q9 P, D: P; k( M
88                         {8 p( l5 o- `6 P9 [% T( z
89                                 $stmt->bindValue(':'.$rowname, $rowvalue);" U- _, S# s0 m, P' s3 C3 @! w
90                         }
* c8 |7 j; z2 M1 s! G6 k当然,另一种选择是输入execute()一组参数,这可以execute()解析绑定部分 (因此是我个人的最爱!) 。
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则