File Coverage

blib/lib/SQL/Inserter.pm
Criterion Covered Total %
statement 177 177 100.0
branch 78 78 100.0
condition 36 36 100.0
subroutine 25 25 100.0
pod 5 5 100.0
total 321 321 100.0


line stmt bran cond sub pod time code
1             package SQL::Inserter;
2              
3 5     5   1041431 use 5.008;
  5         46  
4 5     5   35 use strict;
  5         57  
  5         155  
5 5     5   23 use warnings;
  5         8  
  5         251  
6              
7 5     5   27 use Carp;
  5         29  
  5         426  
8 5     5   28 use Exporter 'import';
  5         10  
  5         14584  
9              
10             =head1 NAME
11              
12             SQL::Inserter - Efficient buffered DBI inserter and fast INSERT SQL builder
13              
14             =head1 VERSION
15              
16             Version 0.04
17              
18             =cut
19              
20             our $VERSION = '0.04';
21              
22             our @EXPORT_OK = qw(simple_insert multi_insert_sql);
23              
24             =head1 SYNOPSIS
25              
26             use SQL::Inserter;
27              
28             my $sql = SQL::Inserter->new(
29             dbh => $dbh,
30             table => 'table',
31             cols => [qw/col1 col2.../],
32             buffer => 100? # Default buffer is 100 rows
33             );
34              
35             # Pass single or multiple rows of data as an array (fastest method):
36             $sql->insert($col1_row1, $col2_row1, $col1_row2...);
37              
38             # Alternatively, pass a single row as a hash, allows SQL code passed as
39             # references instead of values (no need to define cols in constructor):
40             $sql->insert({
41             column1 => $data1,
42             column2 => \'NOW()',
43             ...
44             });
45              
46             # Since the inserts are buffered, they might not have been executed yet.
47             # You can manually flush the buffer at any time with no argument on insert
48             # (otherwise there is auto-flush on the object's destruction):
49             $sql->insert();
50              
51             # There are also functions to just get the SQL statement and its bind vars
52             # similar to SQL::Abstract or SQL::Maker insert, but with much less overhead:
53             my ($sql, @bind) = simple_insert($table, {col1=>$val...});
54              
55             # Multi-row variant:
56             my ($sql, @bind) = simple_insert($table, [{col1=>$val1...},{col1=>$val2...},...]);
57              
58             # Or, construct an SQL statement with placeholders for a given number of rows:
59             my $sql = multi_insert_sql('table', [qw/col1 col2.../], $num_of_rows);
60              
61             =head1 DESCRIPTION
62              
63             SQL::Inserter's main lightweight OO interface will let you do L inserts as
64             efficiently as possible by managing a multi-row buffer and prepared statements.
65              
66             You only have to select the number of rows for the buffered writes (default is 100)
67             and choose whether to pass your data in arrays (fastest, requires all data to be bind
68             values, will execute the same prepared statement every time the buffer is full) or
69             hashes (allows SQL code apart from plain values).
70              
71             It also provides lightweight functions that return the SQL queries to be used manually,
72             similar to C, but much faster.
73              
74             C and C variants supported for MySQL/MariaDB.
75              
76             Although it is developed for use in our production MySQL/MariaDB, its main functions
77             will work on DBs with similar C multi-row syntax like PostgreSQL.
78             From v0.03, Oracle Database is also supported.
79              
80             =head1 EXPORTS
81              
82             On request: C C.
83              
84             =head1 CONSTRUCTOR
85              
86             =head2 C
87              
88             my $sql = SQL::Inserter->new(
89             dbh => $dbh,
90             table => $table,
91             cols => \@column_names?,
92             buffer => 100?,
93             duplicates => $ignore_or_update?,
94             null_undef => $convert_undef_to_NULL?,
95             oracle => $oracle_format?
96             );
97              
98             Creates an object to insert data to a specific table. Buffering is enabled by default
99             and anything left on it will be written when the object falls out of scope / is destroyed.
100              
101             Required parameters:
102              
103             =over 4
104              
105             =item * C : A L database handle.
106              
107             =item * C : The name of the db table to insert to. See L if you
108             are using a restricted word for a table name.
109              
110             =back
111              
112             Optional parameters:
113              
114             =over 4
115              
116             =item * C : The names of the columns to insert. It is required if arrays are
117             used to pass the data. With hashes they are optional (the order will be followed
118             if they are defined). See L if you are using any restricted words for
119             column names.
120              
121             =item * C : Max number of rows to be held in buffer before there is a write.
122             The buffer flushes (writes contents) when the object is destroyed. Setting it to 1
123             writes each row separately (least efficient). For small rows you can set buffer to
124             thousands. The default is a (conservative) 100 which works with big data rows.
125              
126             =item * C : For MySQL, define as C<'ignore'> or C<'update'> to get an
127             C or C query respectively. See L
128             for details on the latter.
129              
130             =item * C : Applies to the hash inserts only. If true, any undefined
131             values will be converted to SQL's C (similar to the C default).
132             The default behaviour will leave an undef as the bind variable, which may either
133             create an empty string in the db or give an error depending on your column type and
134             db settings.
135              
136             =item * C : This is automatically set to true when an Oracle driver is
137             detected (using C<$dbh-E{Driver}-E{Name}>) and the module will produce the
138             Oracle C format. Specifying it manually in the constructor will override
139             the automatic detection - a false value will force the MySQL compatible multi-row
140             C syntax (which should work on Oracle 23c or newer), while a true value
141             will generate the "classic" Oracle syntax even without an Oracle driver detected.
142              
143             =back
144              
145             =cut
146              
147             sub new {
148 18     18 1 584772 my $class = shift;
149 18         82 my %args = @_;
150              
151 18         33 my $self = {};
152 18         39 bless($self, $class);
153              
154 18   100     224 $self->{dbh} = $args{dbh} || croak("dbh parameter (db handle) required.");
155 17   100     181 $self->{table} = $args{table} || croak("table parameter required.");
156 16         36 $self->{cols} = $args{cols};
157 16   100     57 $self->{buffer} = $args{buffer} || 100;
158 16         36 $self->{dupes} = $args{duplicates};
159 16         71 $self->{null} = $args{null_undef};
160 16         33 $self->{oracle} = $args{oracle};
161             $self->{oracle} = 1
162             if !defined $self->{oracle}
163             && $self->{dbh}->{Driver}->{Name}
164 16 100 100     104 && $self->{dbh}->{Driver}->{Name} =~ /oracle/i;
      100        
165              
166 16 100       45 $self->{dupes} = 'oracle' if $self->{oracle};
167 16 100       40 if ($self->{dupes}) {
168 7 100       34 $self->{ignore} = 1 if $self->{dupes} eq "ignore";
169 7 100       22 $self->{update} = 1 if $self->{dupes} eq "update";
170             }
171 16         54 $self->_cleanup();
172              
173 16         57 return $self;
174             }
175              
176             =head1 METHODS
177              
178             =head2 insert
179              
180             # Fastest array method. Only bind data is passed.
181             my $ret = $sql->insert(@column_data_array);
182              
183             # Alternative allows SQL code as values in addition to bind variables.
184             my $ret = $sql->insert(\%row_data);
185              
186             # No parameters will force emptying of buffer (db write).
187             my $ret = $sql->insert();
188              
189             The main insert method. Returns the return value of the last C statement
190             if there was one called, 0 otherwise (buffer not full.
191              
192             It works in two main modes, by passing an array or a hashref:
193              
194             =over 4
195              
196             =item Array mode
197              
198             Pass the data for one or more rows in a flat array, buffering will work automatically
199             based on your C settings. Obviously your C<@column_data_array> has to contain
200             a multiple of the number of C defined on the constructor.
201              
202             This is the fastest mode, but it only allows simple bind values. Any undefined values
203             will be passed directly to DBI->execute, which may or may not be what you expect -
204             there will not be any explicit conversion to SQL C.
205              
206             =item Hash mode
207              
208             Pass a reference to a hash containing the column names & values for a single row
209             of data. If C was not defined on the constructor, the columns from the first
210             data row will be used instead. For subsequent rows any extra columns will be disregarded
211             and any missing columns will be considered to have an C (which can be
212             automatically converted to C if the C option was set).
213              
214             =item Flushing the buffer
215              
216             Calling C with no arguments forces a write to the db, flushing the buffer.
217             You don't have to call this manually as the buffer will be flushed when the object
218             is destroyed (e.g. your object falls out of scope).
219              
220             =item Mixing modes
221              
222             You can theoretically mix modes, but only when the buffer is empty e.g. you can start
223             with the array mode, flush the buffer and continue with hash mode (C will be
224             defined from the array mode). Or you can start with hash mode (so C will be defined
225             from the very first hash), and after flushing the buffer you can switch to array mode.
226              
227             =back
228              
229             =cut
230              
231             sub insert {
232 48     48 1 7912 my $self = shift;
233              
234 48 100 100     292 return $self->_hash_insert(@_) if $_[0] and ref($_[0]);
235              
236             # Empty the buffer with no argument
237 25 100       78 return $self->{buffer_counter} ? $self->_empty_buffer() : 0 unless @_;
    100          
238              
239             croak("Calling insert without a hash requires cols defined in constructor")
240 19 100       151 unless $self->{cols};
241              
242             croak("Insert arguments must be multiple of cols")
243 18 100       29 if scalar(@_) % scalar @{$self->{cols}};
  18         1186  
244              
245             croak("Insert was previously called with hash argument (still in buffer)")
246 16 100       138 if $self->{hash_buffer};
247              
248 15         33 my $ret = 0;
249 15         50 while (@_) {
250 22         34 my $rows = scalar(@_) / scalar @{$self->{cols}};
  22         59  
251 22         48 my $left = $self->{buffer} - $self->{buffer_counter}; # Space left in buffer
252              
253 22 100       73 if ($rows > $left) { # Can't fit buffer
254 7         32 my $max = $left * scalar @{$self->{cols}};
  7         15  
255 7         15 push @{$self->{bind}}, splice(@_,0,$max);
  7         38  
256 7         18 $self->{buffer_counter} = $self->{buffer};
257             } else {
258 15         23 push @{$self->{bind}}, splice(@_);
  15         52  
259 15         32 $self->{buffer_counter} += $rows;
260             }
261 22 100       88 $ret = $self->_write_full_buffer() if $self->{buffer_counter} == $self->{buffer};
262             }
263              
264 15         76 return $ret;
265             }
266              
267             =head2 last_insert_id
268              
269             # MySQL
270             my $id = $sql->last_insert_id;
271              
272             # Depending on the driver you might need parameters
273             my $id = $sql->last_insert_id($catalog, $schema, $table, $field, \%attr);
274              
275             Returns the id of the last insert row, if available, after emptying the buffer.
276              
277             Convenience wrapper around L's database handle method of the same name. See
278             that method's documentation for details and caveats depending on your DB driver.
279              
280             =cut
281              
282             sub last_insert_id {
283 2     2 1 8 my $self = shift;
284              
285 2 100       12 $self->_empty_buffer() if $self->{buffer_counter};
286              
287 2         9 return $self->{dbh}->last_insert_id(@_);
288             }
289              
290             =head1 ATTRIBUTES
291              
292             =head2 C
293              
294             my $val = $sql->{last_retval}
295              
296             The return value of the last DBI C is stored in this attribute. On a successful
297             insert it should contain the number of rows of that statement. Note that an C
298             call, depending on the buffering, may call C zero, one or more times.
299              
300             =head2 C
301              
302             my $total = $sql->{row_total}
303              
304             Basically a running total of the return values, for successful inserts it shows you
305             how many rows were inserted into the database. It will be undef if no C has
306             been called.
307              
308             =head2 C
309              
310             my $count = $sql->{buffer_counter}
311              
312             Check how many un-inserted data rows the buffer currently holds.
313              
314             =head1 FUNCTIONS
315              
316             =head2 simple_insert
317              
318             # Single row
319             my ($sql, @bind) = simple_insert($table, \%fieldvals, \%options);
320              
321             # Multi-row
322             my ($sql, @bind) = simple_insert($table, [\%fieldvals_row1,...], \%options);
323              
324             Returns the SQL statement and bind variable array for a hash containing the row
325             columns and values. Values are treated as bind variables unless they are references
326             to SQL code strings. E.g. :
327              
328             my ($sql, @bind) = simple_insert('table', {foo=>"bar",when=>\"NOW()"});
329             ### INSERT INTO table (foo, when) VALUES (?,NOW())
330              
331             The function also accepts an array of hashes to allow multi-row inserts:
332              
333             my ($sql, @bind) = simple_insert('table', [{foo=>"foo"},{foo=>"bar"}]);
334             ### INSERT INTO table (foo) VALUES (?),(?)
335              
336             The first row (element in array) needs to contain the superset of all the columns
337             that you want to insert, if some of your rows have undefined column data.
338              
339             Options:
340              
341             =over 4
342            
343             =item * C : If true, any undefined values will be converted to SQL's
344             C (similar to the C default). The default behaviour will leave
345             an undef as the bind variable, which may either create an empty string in the db or
346             give an error depending on your column type and db settings.
347              
348             =item * C : For MySQL, define as C<'ignore'> or C<'update'> to get an
349             C or C query respectively. See L
350             for details on the latter.
351              
352             =item * C : Will generate the Oracle C syntax (required for
353             pre-23c Oracle Databases).
354              
355             =back
356              
357             =cut
358              
359             sub simple_insert {
360 18     18 1 265504 my $table = shift;
361 18         126 my $fields = shift;
362 18         20 my $opt = shift;
363 18 100       50 my $join = $opt->{oracle} ? "\n" : ",\n";
364              
365 18         29 my ($placeh, @bind, @cols);
366 18 100       34 if (ref($fields) eq 'ARRAY') {
367 5         23 @cols = keys %{$fields->[0]};
  5         19  
368 5 100       17 my $head = $opt->{oracle} ? _oracle_into_head($table, \@cols) : "";
369 5         9 my @rows;
370 5         47 foreach my $f (@$fields) {
371 9         30 my ($row, @b) = _row_placeholders($f, \@cols, $opt->{null_undef}, $head);
372 9         23 push @rows, $row;
373 9         18 push @bind, @b;
374             }
375 5         16 $placeh = join($join, @rows);
376             } else {
377 13         33 @cols = keys %$fields;
378 13 100       26 my $head = $opt->{oracle} ? _oracle_into_head($table, \@cols) : "";
379 13         44 ($placeh, @bind) = _row_placeholders($fields, \@cols, $opt->{null_undef}, $head);
380             }
381              
382             return _create_insert_sql(
383             $table, \@cols, $placeh, $opt->{duplicates}
384 18 100       58 ), @bind unless $opt->{oracle};
385              
386 7         15 return _oracle_create_insert_sql($placeh), @bind;
387             }
388              
389             =head2 multi_insert_sql
390              
391             my $sql = multi_insert_sql(
392             $table,
393             \@columns, # names of table columns
394             $num_of_rows?, # default = 1
395             $variant? # can be set as 'ignore'/'update' on duplicate key (MySQL) or 'oracle'
396             );
397              
398             Builds bulk insert query (single insert is possible too), with ability for
399             ignore/on duplicate key update variants for MySQL.
400              
401             Requires at least the name of the table C<$table> and an arrayref with the column
402             names C<\@columns>. See L if you want to quote table or column names.
403              
404             Optional parameters:
405              
406             =over 4
407            
408             =item * C<$num_of_rows> : By default it returns SQL with bind value placeholders
409             for a single row. You can define any number of rows to use with multi-row bind
410             variable arrays.
411              
412             =item * C<$variant> : For MySQL, passing C<'ignore'> as the 4th argument returns
413             an C query. Passing C<'update'> as the argument returns a query
414             containing an `ON DUPLICATE KEY UPDATE` clause (see L for further details).
415             Passing C<'oracle'> will create the C syntax for pre-23c Oracle DBs.
416              
417             =back
418              
419             =cut
420              
421             sub multi_insert_sql {
422 27     27 1 17791 my $table = shift;
423 27         45 my $columns = shift;
424 27   100     83 my $num_rows = shift || 1;
425 27         44 my $variant = shift;
426              
427 27 100 100     174 return unless $table && $columns && @$columns;
      100        
428 22 100 100     118 return _oracle_create_insert_sql(
429             _oracle_insert_into($table, $columns) x $num_rows
430             )
431             if $variant && $variant eq 'oracle';
432              
433 16         83 my $placeholders =
434             join(",\n", ('(' . join(',', ('?') x @$columns) . ')') x $num_rows);
435              
436 16         52 return _create_insert_sql($table, $columns, $placeholders, $variant)
437             }
438              
439             ## Private methods
440              
441             sub _set_head {
442 9     9   17 my $self = shift;
443             $self->{row_head} =
444             $self->{oracle}
445             ? _oracle_into_head($self->{table}, $self->{cols})
446 9 100       42 : "";
447             }
448              
449             sub _hash_insert {
450 23     23   38 my $self = shift;
451 23         37 my $fields = shift;
452 23         37 my $ret = 0;
453              
454             croak("Insert was previously called with an array argument (still in buffer)")
455 23 100 100     177 if $self->{buffer_counter} && !$self->{hash_buffer};
456              
457 22         41 $self->{buffer_counter}++;
458 22 100       60 $self->{cols} = [keys %$fields] if !defined($self->{cols});
459 22 100       67 $self->_set_head unless defined $self->{row_head};
460              
461 22         70 my ($row, @bind) = _row_placeholders($fields, $self->{cols}, $self->{null}, $self->{row_head});
462 22         39 push @{$self->{hash_buffer}}, $row;
  22         59  
463 22         32 push @{$self->{bind}}, @bind;
  22         60  
464              
465 22 100       96 $ret = $self->_write_hash_buffer() if $self->{buffer_counter} == $self->{buffer};
466              
467 22         79 return $ret;
468             }
469              
470             sub _write_full_buffer {
471 14     14   270 my $self = shift;
472              
473             $self->{full_buffer_insert} = $self->_prepare_full_buffer_insert()
474 14 100       52 if !$self->{full_buffer_insert};
475              
476 14         104 $self->_execute($self->{full_buffer_insert});
477 14         48 $self->_cleanup();
478              
479 14         50 return $self->{last_retval};
480             }
481              
482             sub _prepare_full_buffer_insert {
483 8     8   16 my $self = shift;
484             $self->{full_buffer_insert} = $self->{dbh}->prepare(
485 8         26 multi_insert_sql(map {$self->{$_}} qw/table cols buffer dupes/)
  32         81  
486             );
487             }
488              
489             sub _empty_buffer {
490 8     8   44 my $self = shift;
491              
492 8 100       30 return $self->_write_hash_buffer() if $self->{hash_buffer};
493              
494 5         11 my $rows = scalar(@{$self->{bind}}) / scalar @{$self->{cols}};
  5         20  
  5         17  
495             my $sth = $self->{dbh}->prepare(
496             multi_insert_sql(
497             $self->{table},
498             $self->{cols},
499             $rows,
500             $self->{dupes}
501             )
502 5         23 );
503 5         46 $self->_execute($sth);
504 5         35 $self->_cleanup();
505              
506 5         16 return $self->{last_retval};
507             }
508              
509             sub _write_hash_buffer {
510 13     13   23 my $self = shift;
511 13 100       43 my $join = $self->{oracle} ? "\n" : ",\n";
512 13         24 my $placeh = join($join, @{$self->{hash_buffer}});
  13         45  
513             my $sth = $self->{dbh}->prepare(
514             $self->{oracle}
515             ? _oracle_create_insert_sql($placeh)
516             : _create_insert_sql($self->{table}, $self->{cols}, $placeh, $self->{dupes})
517 13 100       117 );
518 13         99 $self->_execute($sth);
519 13         36 $self->_cleanup();
520              
521 13         41 return $self->{last_retval};
522             }
523              
524             sub _execute {
525 32     32   51 my $self = shift;
526 32         48 my $sth = shift;
527              
528 32 100       136 $self->{row_total} = 0 if !defined($self->{row_total});
529 32         53 $self->{last_retval} = $sth->execute(@{$self->{bind}});
  32         106  
530 32 100       282 $self->{row_total} += $self->{last_retval} if $self->{last_retval};
531             }
532              
533             sub _cleanup {
534 48     48   83 my $self = shift;
535 48         108 $self->{bind} = undef;
536 48         116 $self->{hash_buffer} = undef;
537 48         93 $self->{buffer_counter} = 0;
538             }
539              
540             sub DESTROY {
541 18     18   27594 my $self = shift;
542             # Empty buffer
543 18 100       124 $self->_empty_buffer() if $self->{buffer_counter};
544             }
545              
546             ## Private functions
547              
548             sub _oracle_into_head {
549 15     15   22 my $table = shift;
550 15         17 my $cols = shift;
551 15         91 return "INTO $table(".join(",", @$cols).") VALUES";
552             }
553              
554             sub _oracle_insert_into {
555 6     6   11 my $table = shift;
556 6         10 my $cols = shift;
557 6         16 return _oracle_into_head($table, $cols)."(".join(',', ('?') x @$cols).")\n";
558             }
559              
560             sub _oracle_create_insert_sql {
561 17     17   31 my $placeh = shift;
562 17         32 chomp($placeh);
563              
564 17         75 return "INSERT ALL\n$placeh\nSELECT 1 FROM dual";
565             }
566              
567             sub _create_insert_sql {
568 40     40   2442 my $table = shift;
569 40         57 my $columns = shift;
570 40         62 my $placeh = shift;
571 40   100     202 my $variant = shift || "";
572              
573 40 100       105 my $ignore = ($variant eq 'ignore') ? ' IGNORE' : '';
574 40         145 my $cols = join(',', @$columns);
575 40         92 my $sql = "INSERT$ignore INTO $table ($cols)\nVALUES $placeh";
576              
577 40 100       88 $sql .= _on_duplicate_key_update($columns) if $variant eq 'update';
578              
579 40         285 return $sql;
580             }
581              
582             sub _row_placeholders {
583 52     52   179467 my $fields = shift;
584 52         70 my $cols = shift;
585 52         86 my $null = shift;
586 52         70 my $head = shift;
587 52 100       124 my $sql = $head ? "$head(" : "(";
588 52         84 my @bind = ();
589 52         58 my $val;
590              
591 52         105 foreach my $key (@$cols) {
592 80 100 100     210 $fields->{$key} = \"NULL" if $null && !defined($fields->{$key});
593              
594 80 100       154 if (ref($fields->{$key})) {
595 17         21 $val = ${$fields->{$key}};
  17         33  
596             } else {
597 63         151 $val = "?";
598 63         109 push @bind, $fields->{$key};
599             }
600 80         260 $sql .= "$val,";
601             }
602              
603 52 100       171 chop($sql) if @$cols;
604              
605 52         221 return "$sql)", @bind;
606             }
607              
608             sub _on_duplicate_key_update {
609 8     8   3011 my $columns = shift;
610             return "\nON DUPLICATE KEY UPDATE "
611 8         28 . join(',', map {"$_=VALUES($_)"} @$columns);
  11         85  
612             }
613              
614             =head1 NOTES
615              
616             =head2 Using reserved words as object names
617              
618             If you are using reserved words as table/column names (which is strongly discouraged),
619             just include the appropriate delimiter in the C or C parameter. E.g. for
620             MySQL with columns named C and C you can do:
621              
622             cols => [qw/`from` `to`/]
623              
624             For PostgreSQL or Oracle you'd do C<[qw/"from" "to"/]>, for SQL Server C<[qw/[from] [to]/]> etc.
625              
626             =head2 On duplicate key update
627              
628             The C 'update'> option creates an C clause
629             for the query. E.g.:
630              
631             my $sql = multi_insert_sql('table_name', [qw/col1 col2/], 2, 'update');
632              
633             will produce:
634              
635             ## INSERT INTO table_name (col1,col2) VALUES (?,?),(?,?) ON DUPLICATE KEY UPDATE col1=VALUES(col1),col2=VALUES(col2)
636              
637             Note that as of MySQL 8.0.20 the C in C is deprecated (row alias is
638             used instead), so this functionality might need to be updated some day if C is
639             removed completely.
640              
641             =head2 Output whitespace
642              
643             No spaces are added to the output string beyond the minimum. However, there is a new
644             line (C<\n>) added for each row of value placeholders - mainly to easily count the
645             number of rows from the string.
646             Also, the C clause is on a new line.
647              
648             =head2 Error handling
649              
650             The module does not do any error handling on C/C statements,
651             you should use L's C and C.
652              
653             =head2 Performance
654              
655             The OO interface has minimal overhead. The only consideration is that if your rows
656             do not contain particularly large amounts of data, you may want to increase the buffer
657             size which is at a modest 100 rows.
658              
659             Internally, to construct the prepared statements it uses similar logic to the public
660             functions. C is of particular interest as it is a minimalistic function
661             that may replace (similar interface / feature set) the C functions from
662             C or C while being over 40x faster than the former and
663             around 3x faster than the latter. The included C script gives
664             an idea (results on an M1 Pro Macbook):
665              
666             Compare SQL::Abstract, SQL::Maker, simple_insert:
667             Rate Abstract Abstract cached Maker Maker cached simple_insert
668             Abstract 4207/s -- -6% -90% -91% -98%
669             Abstract cached 4482/s 7% -- -90% -90% -98%
670             Maker 44245/s 952% 887% -- -4% -76%
671             Maker cached 46205/s 998% 931% 4% -- -75%
672             simple_insert 187398/s 4355% 4081% 324% 306% --
673            
674             Compare simple_insert, multi_insert_sql for single row:
675             Rate simple_insert multi_insert_sql
676             simple_insert 190037/s -- -76%
677             multi_insert_sql 797596/s 320% --
678              
679             =head1 AUTHOR
680              
681             Dimitrios Kechagias, C<< >>
682              
683             =head1 BUGS
684              
685             Please report any bugs or feature requests either on L (preferred), or on RT
686             (via the email , or L).
687              
688             I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
689              
690             =head1 GIT
691              
692             L
693              
694             =head1 CPAN
695              
696             L
697              
698             =head1 LICENSE AND COPYRIGHT
699              
700             Copyright (C) 2023, SpareRoom
701              
702             This is free software; you can redistribute it and/or modify it under
703             the same terms as the Perl 5 programming language system itself.
704              
705             =cut
706              
707             1; # End of SQL::Inserter