Notes and updates based on riba's most recent comments
[dbsrgits/DBIx-Class-Manual-SQLHackers.git] / lib / DBIx / Class / Manual / SQLHackers / SELECT.pod
CommitLineData
6c2a4396 1=head1 NAME
2
3DBIx::Class::Manual::SQLHackers::SELECT - DBIx::Class for SQL Hackers - SELECT
4
5=over
6
7=item L<Introduction|DBIx::Class::Manual::SQLHackers::Introduction>
8
9=item L<CREATE|DBIx::Class::Manual::SQLHackers::CREATE>
10
11=item L<INSERT|DBIx::Class::Manual::SQLHackers::INSERT>
12
13=item SELECT
14
15=item L<UPDATE|DBIx::Class::Manual::SQLHackers::UPDATE>
16
17=item L<DELETE|DBIx::Class::Manual::SQLHackers::DELETE>
18
19=item L<BEGIN, COMMIT|DBIx::Class::Manual::SQLHackers::Transactions>
20
21=back
22
23=head1 SELECTing data
24
25=head2 Fetching rows from a query
26
27 SELECT id, username, dob, realname, password
28 FROM users;
29
2f41b1a9 30In DBIx::Class queries (or more specifically query plans) are represented by ResultSet objects. These are created by calling B<search> on existing resultsets, while passing new search conditions or attributes. A query is not run against the database until data is explicitly requested.
6c2a4396 31
32You can either fetch all the data at once, or iterate over the results:
33
34=over
35
36=item 1. Create a Schema object representing the database you are working with:
37
38 my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
39
2f41b1a9 40=item 2. The B<resultset> method returns a ResultSet representing a query retrieving all columns of the given B<ResultSource> without conditions:
6c2a4396 41
42 my $user_resultset = $schema->resultset('User');
43
44=item 3. Fetch all users as Row objects using the B<all> method:
45
46 my @users = $user_resultset->all();
47
48=item 4. OR, fetch each user as a Row object using B<next>:
49
50 while( my $user = $user_resultset->next()) {
51 }
52
53=back
54
55=head2 Fetching column values from a Row object
56
2f41b1a9 57The Row object represents the results from a single data source in the query. The column values can be retrieved by using the accessor methods named after the column names. (By default that is, accessors can be changed in the L<Result Class|DBIx::Class::ResulSource> if needed).
6c2a4396 58
59 print $user->username;
60
abc32120 61See the L<DBIx::Class::Row> documentation for more things you can do
6c2a4396 62with Row objects.
63
64=head2 Simple SELECT, one row via the primary key
65
66 SELECT id, username, dob, realname, password
67 FROM users
68 WHERE id = 1;
69
70The B<find> method on a ResultSet is a shortcut to create a query based on the inputs, run the query, and return a single row as a Row object result.
71
72If passed a condition which matches multiple rows, a warning is given.
73
74=over
75
76=item 1. Create a Schema object representing the database you are working with:
77
78 my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
79
80=item 2. Call the B<find> method on the resultset for the L<ResultSource|DBIx::Class::ResultSource> you wish to fetch data from:
81
82 my $fred_user = $schema->resultset('User')->find({ id => 1 });
83
84=back
85
86B<$fred_user> is a now Row object.
87
88=head2 Simple SELECT, one row via a unique key
89
90 SELECT id, username, dob, realname, password
91 FROM users
92 WHERE username = 'fredbloggs';
93
abc32120 94B<find> also works well on unique constraints, for example the username of our user. Unique constraints can be defined on Result classes using B<add_unique_constraint> (See L<CREATE|DBIx::Class::Manual::SQLHackers::CREATE>).
6c2a4396 95
96=over
97
98=item 1. Create a Schema object representing the database you are working with:
99
100 my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
101
102=item 2. Call the B<find> method on the resultset for the L<ResultSource|DBIx::Class::ResultSource> you wish to fetch data from:
103
104 my $fred_user = $schema->resultset('User')->find(
105 { username => 'fredbloggs' },
106 { key => 'uniq_username' }
107 );
108
109=back
110
111"uniq_username" is the name of a constraint defined on the User L<ResultSource|DBIx::Class::ResultSource> which specifies that the username column is unique across the table. The second argument to B<find> is a set of attributes, of which the "key" attribute defines which constraint to do a lookup on.
112
113=head2 Simple SELECT, with WHERE condition
114
115 SELECT id, username, dob, realname, password
116 FROM users
117 WHERE dob = '1910-02-01';
118
119To select all users born on the date '1910-02-01', we can use the B<search> method to prepare a query. Search returns a new resultset with the search conditions stored in it, it does not run the query on the database.
120
121=over
122
123=item 1. Create a Schema object representing the database you are working with:
124
125 my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
126
127=item 2. Call the B<search> method on the resultset for the L<ResultSource|DBIx::Class::ResultSource> you wish to fetch data from:
128
129 my $dob_search = $schema->resultset('User')->search(
130 { dob => '1910-02-01' }
131 );
132
133=back
134
abc32120 135To run the query, use the B<all> or B<next> methods shown at the beginning of this page.
6c2a4396 136
137=head2 SELECT with different WHERE conditions
138
139Below are shown some common SQL where conditions. The syntax for these is parsed by a module called L<SQL::Abstract> which DBIx::Class uses. They can all be passed to the B<search> method as conditions.
140
141 SELECT id, username, dob, realname, password
142 FROM users
143 WHERE username LIKE 'fred%';
144
145 my $name_search = $schema->resultset('User')->search(
146 { username => { '-like' => 'fred%' } }
147 );
148
149 SELECT id, username, dob, realname, password
150 FROM users
151 WHERE dob BETWEEN '1910-01-01' AND '1910-12-31';
152
153 my $year_dob_search = $schema->resultset('User')->search(
154 { dob => { '-between' => ['1910-01-01', '1910-12-31'] } }
155 );
156
157 SELECT id, username, dob, realname, password
158 FROM users
159 WHERE dob IN ('1910-02-01', '1910-02-02');
160
161 my $feb_dob_search = $schema->resultset('User')->search(
162 { dob => { '-in' => ['1910-02-01', '1910-02-02'] } }
163 );
164
165 SELECT id, username, dob, realname, password
166 FROM users
167 WHERE dob >= 1911-01-01;
168
169 my $next_year_dob = $schema->resultset('User')->search(
170 { dob => { '>=', '1911-01-01' } }
171 );
172
173
174=head2 SELECT with WHERE condition on JOINed table
175
176 SELECT posts.id, created_date, title, post
177 FROM posts
178 JOIN users user ON user.id = posts.user_id
179 WHERE user.username = 'fredbloggs';
180
181The second argument to B<search> is a hashref of attributes to apply to the query. One of them is B<join>, which is used to connect to other tables using the relationships defined in the Result classes.
182
183=over
184
185=item 1. Create a Schema object representing the database you are working with:
186
187 my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
188
189=item 2. Call the B<search> method on the resultset for the L<ResultSource|DBIx::Class::ResultSource> you wish to fetch data from:
190
191 my $freds_posts = $schema->resultset('Post')->search(
192 { 'user.username' => 'fredbloggs' },
193 { join => 'user' }
194 );
195
196=back
197
198Note that the string "user", used twice here, refers to the B<name> of the L<Relationship|DBIx::Class::Manual::SQLHackers::CREATE> between the "Post" source and the "User" source. All dealings with related tables are refered to by relationship names, not table names.
199
200To run the query, use the B<all> or B<next> methods show at the beginning of this page.
201
202=head2 SELECT with fewer columns
203
204 SELECT id, title
205 FROM posts
206
207There's usually little reason to do this sort of query, as fetching all the data in a row doesn't cost any more time than fetching some of it. Unless of course your source is a View with calculations, or has huge blobs, or.. Okay, you might well want to do this occasionally.
208
0e8be732 209B<find> will always pull all the columns for the found row, so use the B<search> method for this.
6c2a4396 210
211=over
212
213=item 1. Create a Schema object representing the database you are working with:
214
215 my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
216
217=item 2. Call the B<search> method on the resultset for the L<ResultSource|DBIx::Class::ResultSource> you wish to fetch data from:
218
219 my $post_titles = $schema->resultset('Post')->search(
220 { },
221 { columns => [qw/id title/] }
222 );
223
224=back
225
2f41b1a9 226Note that accessors for other columns not fetched will return B<undef>, which is also the perl equivalent of the SQL C<NULL> value. To disambiguate between an C<undef> meaning "this column is set null" and "we never retrieved the value of this column" use L<DBIx::Class::Row/has_column_loaded>.
6c2a4396 227
228
229=head2 SELECT with aggregates
230
231 SELECT COUNT(*)
232 FROM users;
233
234To find out how many users exist. This simple one can be achieved with a built-in method, B<count>.
235
236=over
237
238=item 1. Create a Schema object representing the database you are working with:
239
240 my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
241
242=item 2. Call the *count* method on the resultset for the [Source] you wish to fetch data from:
243
244 my $posts_count = $schema->resultset('Post')->count();
245
246=back
247
248The result is not an object, just a number.
249
250 SELECT SUM(amount)
251 FROM prices;
252
253A rather pointless exercise in summing an entire "amount" column from an imaginary "prices" table. This can be done in several ways, first, the built-in L<DBIx::Class::ResultSet::Column> method, by calling B<get_column>.
254
255=over
256
257=item 1. Create a Schema object representing the database you are working with:
258
259 my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
260
261=item 2. Call the B<get_column> method on the resultset for the L<ResultSource|DBIx::Class::ResultSource> you wish to fetch data from, then the B<sum> method:
262
263 my $sum_prices = $schema->resultset('Price')->get_column('amount')
264 ->sum();
265
266=back
267
268The result is just a number.
269
270The alternate way uses the B<search> method and is easier to build further refinements into.
271
272=over
273
274=item 1. Create a Schema object representing the database you are working with:
275
276 my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
277
278=item 2. Call the B<search> method on the resultset for the L<ResultSource|DBIx::Class::ResultSource> you wish to fetch data from:
279
280 my $sum_prices_rs = $schema->resultset('Price')->search(
281 { },
abc32120 282 { columns => { sum_amount => { SUM => 'amount'} } },
6c2a4396 283 );
284
285=back
286
287The result is a resultset. To fetch the one-row result, call B<single> or B<all>. The resulting Row object will not contain an accessor for the virtual "sum_amount" column, we'll need to fetch it using the Row method B<get_column>.
288
289 print $sum_prices_rs->single->get_column('sum_amount');
290
291=head2 SELECT from JOINed tables
292
293 SELECT users.id, username, posts.id, posts.title
294 FROM users
295 JOIN posts posts ON posts.used_id = users.id
296
297To select data from other tables, use the B<join> attribute to name the table relationships to create a JOIN clause to.
298
299=over
300
301=item 1. Create a Schema object representing the database you are working with:
302
303 my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
304
305=item 2. Call the B<search> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to group data on:
306
307 my $posts_count_per_user = $schema->resultset('User')->search(
308 { },
abc32120 309 { columns => [ qw/id username posts.id posts.title/ ],
6c2a4396 310 join => 'posts',
311 }
312 );
313
314=back
315
316Here "posts" refers to the name of the L<Relationship|DBIx::Class::Relationship> between the "User" source and the "Post" source.
317
318To retrieve the extra data, call the usual relationship accessor:
319
320 while( my $row = $sorted_users->next) {
321 print "user/post: ", $row->username;
322 print $_->title for $row->posts;
323 print "\n";
324 }
325
326
327=head2 SELECT with GROUP BY
328
329 SELECT users.id, username, COUNT(posts.id)
330 FROM users
331 JOIN posts posts ON posts.used_id = users.id
332 GROUP BY users.id, username;
333
abc32120 334To group your results, use the B<group_by> attribute on a B<search> method. We also use the B<columns> attribute to select and name a subset of columns.
6c2a4396 335
336=over
337
338=item 1. Create a Schema object representing the database you are working with:
339
340 my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
341
342=item 2. Call the *search* method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to group data on:
343
344 my $posts_count_per_user = $schema->resultset('User')->search(
345 { },
abc32120 346 { columns => [ qw/id username/, { post_count => { count => 'posts.id' } } ],
6c2a4396 347 join => 'posts',
348 group_by => [qw/id username/],
349 }
350 );
351
352=back
353
354Here "posts" refers to the name of the L<Relationship|DBIx::Class::Relationship> between the "User" source and the "Post" source.
355
356The results will contain two columns with the usual accessors, "id" and "username", and one with no accessor, as it is a virtual column.
357
358 while( my $row = $posts_count_per_user->next) {
359 print "user: ", $row->username, " posts: ", $row->get_column('post_count');
360 }
361
362Note: Remember to disambiguate the columns when joining two tables with identical column names.
363
0e8be732 364=begin comment
6c2a4396 365
0e8be732 366Commented out section as ordering by a related source does not work yet. Fix in progress, will comment back in when DBIC is updated.
6c2a4396 367
0e8be732 368 =head2 SELECT with simple ORDER BY
6c2a4396 369
0e8be732 370 SELECT users.id, username, dob, realname, password, posts.title
371 FROM users
372 JOIN posts posts ON posts.used_id = users.id
373 ORDER BY username, posts.title;
6c2a4396 374
0e8be732 375 To sort the results, use the B<order_by> attributes on a B<search> method. Content can of course be ordered by columns in the current table, or in a joined table
6c2a4396 376
0e8be732 377 =over
6c2a4396 378
0e8be732 379 =item 1. Create a Schema object representing the database you are working with:
6c2a4396 380
0e8be732 381 my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
6c2a4396 382
0e8be732 383 =item 2. Call the B<search> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to sort data on:
6c2a4396 384
0e8be732 385 my $sorted_users = $schema->resultset('User')->search(
386 { },
387 { '+columns' => [ qw/posts.id posts.title/ ],
388 join => 'posts',
389 order_by => [qw/username posts.title/],
390 }
391 );
6c2a4396 392
0e8be732 393 =back
6c2a4396 394
0e8be732 395 Here "posts" refers to the name of the L<Relationship|DBIx::Class::Relationship> between the "User" source and the "Post" source.
6c2a4396 396
0e8be732 397 The results will be ordered by username, then post title, ready for outputting.
6c2a4396 398
0e8be732 399 Note how we have added the title of each post, this prevents us having to fire off a second query to fetch the post data to output it. The B<+columns> attribute specifies an extended set of columns to fetch, in addition to the columns of the main query table.
400
401 To retrieve the extra data, call the usual relationship accessor:
402
403 while( my $row = $sorted_users->next) {
404 print "user/post: ", $row->username;
405 print $_->title for $row->posts;
406 print "\n";
407 }
408
409=end comment
6c2a4396 410
411=head2 SELECT with HAVING
412
413 SELECT users.id, username, dob
414 FROM users
415 JOIN posts posts ON posts.used_id = users.id
abc32120 416 GROUP BY users.id, username, dob
6c2a4396 417 HAVING count(posts.id) = 1
418
419To add a B<having> clause to your query, use the corresponding B<having> attribute.
420
421=over
422
423=item 1. Create a Schema object representing the database you are working with:
424
425 my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
426
427=item 2. Call the B<search> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to filter data on:
428
429 my $filtered_users = $schema->resultset('User')->search(
430 { },
abc32120 431 { 'columns' => [ qw/me.id me.username me.dob/ ],
6c2a4396 432 join => 'posts',
abc32120 433 group_by => [qw/me.id me.username me.dob/],
6c2a4396 434 having => [{ 'posts.id' => 1 }],
435 }
436 );
437
438=back
439
440Here "posts" refers to the name of the L<Relationship|DBIx::Class::Relationship> between the "User" source and the "Post" source.
441
442The results will be filtered by the HAVING clause.
443
444=head2 SELECT with DISTINCT
2f41b1a9 445^^ you may or may not want to mention the distinct => 1 flag, which is an automatic "group by the selection" thing.
6c2a4396 446
447 SELECT DISTINCT(posts.title)
448 FROM posts
449
abc32120 450To produce DISTINCT clauses, we need to use a hashref argument to the list of items passed to the B<columns> attribute.
6c2a4396 451
452=over
453
454=item 1. Create a Schema object representing the database you are working with:
455
456 my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
457
abc32120 458=item 2. Call the B<search> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to find distinct rows on:
6c2a4396 459
460 my $distinct_posts = $schema->resultset('Post')->search(
461 { },
abc32120 462 { columns => [{ 'd_title' => { distinct => 'me.title' } }],
6c2a4396 463 }
464 );
465
466=back
467
abc32120 468This can also be achieved by using the ResultSet method B<get_column>. The method returns a ResultSetColumn object based on the given column name argument, which can call SQL aggregate functions based upon the column of that data.
6c2a4396 469
abc32120 470So we can also do this, for single column DISTINCT clauses:
471
472=over
473
474=item 1. Create a Schema object representing the database you are working with:
475
476 my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
477
478=item 2. Call the B<get_column> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to find distinct rows on:
479
480 my $rs_column = $schema->resultset('Post')->get_column('title');
481
482=item 3. Call the B<func> method on the resultset column object and pass it the name of the function to apply:
483
484 my $titles = $rs_column->func('distinct');
485
486=back
487
488The result will be an arrayref of the actual values. If a ResultSet object is needed for further refinement, use B<func_rs> instead.
6c2a4396 489
490=head2 SELECT ... FOR UPDATE
491
492 SELECT users.id, users.username, users.dob
493 FROM users
494 FOR UPDATE
495
496To fetch data and lock it for updating from other transactions, use the B<for> attribute and pass it the value B<update>. This should be done inside a L<Transaction|DBIx::Class::Manual::SQLHackers::Transaction>.
497
498=over
499
500=item 1. Create a Schema object representing the database you are working with:
501
502 my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
503
504=item 2. Call the B<search> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to lock data on:
505
506 my $locked_posts = $schema->resultset('User')->search(
507 { },
508 { columns => [qw/me.id me.username me.dob/],
509 for => 'update'
510 }
511 );
512
513=back
514
515The resultset and rows will be returned as normal, and can be used to update the rows without worrying about other
516
abc32120 517=head2 SELECT with LIMIT and OFFSET
6c2a4396 518
519 SELECT users.id, users.username
520 FROM users
521 ORDER BY user.dob DESC
abc32120 522 LIMIT 10 OFFSET 11;
6c2a4396 523
abc32120 524To reduce the set of rows fetched, use the B<rows> and B<page> attributes. The value of B<page> will default to 1, which means no OFFSET will be applied.
6c2a4396 525
526=over
527
528=item 1. Create a Schema object representing the database you are working with:
529
530 my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
531
532=item 2. Call the B<search> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to limit data on:
533
534 my $limited_posts = $schema->resultset('User')->search(
535 { },
536 { columns => [qw/me.id me.username/],
537 order_by => { '-desc' => ['user.dob'] },
538 rows => 10,
abc32120 539 page => 2,
6c2a4396 540 }
541 );
542
abc32120 543This will return exactly 10 row objects, sorted by descending date of birth of the users, starting at the 11th row of the sorted result.
6c2a4396 544
545=back
546