Updated according to ribasushi's review.
[dbsrgits/DBIx-Class-Manual-SQLHackers.git] / lib / DBIx / Class / Manual / SQLHackers / SELECT.pod
1 =head1 NAME
2
3 DBIx::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
30 In DBIx::Class queries are represented by ResultSet objects. These are created by calling B<search> on existing resultsets, passing new search conditions. A query is not run against the database until data is explicitly requested. 
31
32 You 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
40 =item 2. The B<resultset> method returns a ResultSet representing a query with no conditions on the given B<ResultSource>:
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
57 The Row object represents the results from a single data source table 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).
58
59     print $user->username;
60
61 See the L<DBIx::Class::Row> documentation for more things you can do
62 with 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
70 The 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
72 If 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
86 B<$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
94 B<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>).
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
119 To 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
135 To run the query, use the B<all> or B<next> methods shown at the beginning of this page.
136
137 =head2 SELECT with different WHERE conditions
138
139 Below 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
181 The 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
198 Note 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
200 To 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
207 There'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
209 # this is completely false, is there a doc that states this that we need to fix?
210 # find() takes all search() attributes, including things like prefetch
211 B<find> will always pull all the columns for the found row, so use the *search* method for this.
212
213 =over
214
215 =item 1. Create a Schema object representing the database you are working with:
216
217         my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
218
219 =item 2. Call the B<search> method on the resultset for the L<ResultSource|DBIx::Class::ResultSource> you wish to fetch data from:
220
221         my $post_titles = $schema->resultset('Post')->search(
222           { },
223           { columns => [qw/id title/] }
224         );
225
226 =back
227
228 Note that accessors for other columns not fetched will return B<undef>, which is also the perl equivalent of the SQL C<NULL> value. To discover whether a columns data has been loaded or not, use L<DBIx::Class::Row/has_column_loaded>.
229     
230
231 =head2 SELECT with aggregates
232
233     SELECT COUNT(*)
234     FROM users;
235
236 To find out how many users exist. This simple one can be achieved with a built-in method, B<count>.
237
238 =over
239
240 =item 1. Create a Schema object representing the database you are working with:
241
242         my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
243
244 =item 2. Call the *count* method on the resultset for the [Source] you wish to fetch data from:
245
246         my $posts_count = $schema->resultset('Post')->count();
247
248 =back
249
250 The result is not an object, just a number.
251
252     SELECT SUM(amount)
253     FROM prices;
254
255 A 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>.
256
257 =over
258
259 =item 1. Create a Schema object representing the database you are working with:
260
261         my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
262
263 =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:
264
265         my $sum_prices = $schema->resultset('Price')->get_column('amount')
266           ->sum();
267
268 =back
269
270 The result is just a number.
271
272 The alternate way uses the B<search> method and is easier to build further refinements into.
273
274 =over
275
276 =item 1. Create a Schema object representing the database you are working with:
277
278         my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
279
280 =item 2. Call the B<search> method on the resultset for the L<ResultSource|DBIx::Class::ResultSource> you wish to fetch data from:
281
282         my $sum_prices_rs = $schema->resultset('Price')->search(
283           { },
284           { columns => { sum_amount => { SUM => 'amount'} } },
285         );
286
287 =back
288
289 The 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>.
290
291     print $sum_prices_rs->single->get_column('sum_amount');
292
293 =head2 SELECT from JOINed tables
294
295     SELECT users.id, username, posts.id, posts.title
296     FROM users
297     JOIN posts posts ON posts.used_id = users.id
298     
299 To select data from other tables, use the B<join> attribute to name the table relationships to create a JOIN clause to.
300
301 =over
302
303 =item 1. Create a Schema object representing the database you are working with:
304
305         my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
306
307 =item 2. Call the B<search> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to group data on:
308
309         my $posts_count_per_user = $schema->resultset('User')->search(
310           { },
311           { columns => [ qw/id username posts.id posts.title/ ],
312             join => 'posts',
313           }
314         );
315
316 =back
317
318 Here "posts" refers to the name of the L<Relationship|DBIx::Class::Relationship> between the "User" source and the "Post" source.
319
320 To retrieve the extra data, call the usual relationship accessor:
321
322     while( my $row = $sorted_users->next) {
323       print "user/post: ", $row->username;
324       print $_->title for $row->posts;
325       print "\n";
326     }
327
328
329 =head2 SELECT with GROUP BY
330
331     SELECT users.id, username, COUNT(posts.id)
332     FROM users
333     JOIN posts posts ON posts.used_id = users.id
334     GROUP BY users.id, username;
335
336 To 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.
337
338 =over
339
340 =item 1. Create a Schema object representing the database you are working with:
341
342         my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
343
344 =item 2. Call the *search* method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to group data on:
345
346         my $posts_count_per_user = $schema->resultset('User')->search(
347           { },
348           { columns => [ qw/id username/, { post_count => { count => 'posts.id' } } ],
349             join => 'posts',
350             group_by => [qw/id username/],
351           }
352         );
353
354 =back
355
356 Here "posts" refers to the name of the L<Relationship|DBIx::Class::Relationship> between the "User" source and the "Post" source.
357
358 The results will contain two columns with the usual accessors, "id" and "username", and one with no accessor, as it is a virtual column.
359
360     while( my $row = $posts_count_per_user->next) {
361       print "user: ", $row->username, " posts: ", $row->get_column('post_count');
362     }
363
364 Note: Remember to disambiguate the columns when joining two tables with identical column names.
365
366 =head2 SELECT with simple ORDER BY
367
368     SELECT users.id, username, dob, realname, password, posts.title
369     FROM users
370     JOIN posts posts ON posts.used_id = users.id
371     ORDER BY username, posts.title;
372
373 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
374
375 =over
376
377 =item 1. Create a Schema object representing the database you are working with:
378
379         my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
380
381 =item 2. Call the B<search> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to sort data on:
382
383 # this will not collapse results (you will get users * (amount of posts || 1) as a result, sure you want to showcase this?
384         my $sorted_users = $schema->resultset('User')->search(
385           { },
386           { '+columns' => [ qw/posts.id posts.title/ ],
387             join => 'posts',
388             order_by => [qw/username posts.title/],
389           }
390         );
391
392 =back
393
394 Here "posts" refers to the name of the L<Relationship|DBIx::Class::Relationship> between the "User" source and the "Post" source.
395
396 The results will be ordered by username, then post title, ready for outputting.
397
398 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.
399
400 To retrieve the extra data, call the usual relationship accessor:
401
402     while( my $row = $sorted_users->next) {
403       print "user/post: ", $row->username;
404       print $_->title for $row->posts;
405       print "\n";
406     }
407
408 =head2 SELECT with HAVING
409
410     SELECT users.id, username, dob
411     FROM users
412     JOIN posts posts ON posts.used_id = users.id
413     GROUP BY users.id, username, dob
414     HAVING count(posts.id) = 1
415
416 To add a B<having> clause to your query, use the corresponding B<having> attribute.
417
418 =over
419
420 =item 1. Create a Schema object representing the database you are working with:
421
422         my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
423
424 =item 2. Call the B<search> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to filter data on:
425
426         my $filtered_users = $schema->resultset('User')->search(
427           { },
428           { 'columns' => [ qw/me.id me.username me.dob/ ],
429             join => 'posts',
430             group_by => [qw/me.id me.username me.dob/],
431             having   => [{ 'posts.id' => 1 }],
432           }
433         );
434
435 =back
436
437 Here "posts" refers to the name of the L<Relationship|DBIx::Class::Relationship> between the "User" source and the "Post" source.
438
439 The results will be filtered by the HAVING clause.
440
441 =head2 SELECT with DISTINCT
442
443     SELECT DISTINCT(posts.title)
444     FROM posts
445
446 To produce DISTINCT clauses, we need to use a hashref argument to the list of items passed to the B<columns> attribute.
447
448 =over
449
450 =item 1. Create a Schema object representing the database you are working with:
451
452         my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
453
454 =item 2. Call the B<search> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to find distinct rows on:
455
456         my $distinct_posts = $schema->resultset('Post')->search(
457           { },
458           { columns => [{ 'd_title' => { distinct => 'me.title' } }],
459           }
460         );
461
462 =back
463
464 This 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.
465
466 So we can also do this, for single column DISTINCT clauses:
467
468 =over
469
470 =item 1. Create a Schema object representing the database you are working with:
471
472         my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
473
474 =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:
475
476         my $rs_column = $schema->resultset('Post')->get_column('title');
477
478 =item 3. Call the B<func> method on the resultset column object and pass it the name of the function to apply:
479
480         my $titles = $rs_column->func('distinct');
481
482 =back
483
484 The result will be an arrayref of the actual values. If a ResultSet object is needed for further refinement, use B<func_rs> instead.
485
486 =head2 SELECT ... FOR UPDATE
487
488     SELECT users.id, users.username, users.dob
489     FROM users
490     FOR UPDATE
491
492 To 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>.
493
494 =over
495
496 =item 1. Create a Schema object representing the database you are working with:
497
498         my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
499
500 =item 2. Call the B<search> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to lock data on:
501
502         my $locked_posts = $schema->resultset('User')->search(
503           { },
504           { columns => [qw/me.id me.username me.dob/],
505             for     => 'update'
506           }
507         );
508
509 =back
510
511 The resultset and rows will be returned as normal, and can be used to update the rows without worrying about other 
512
513 =head2 SELECT with LIMIT and OFFSET
514
515     SELECT users.id, users.username
516     FROM users
517     ORDER BY user.dob DESC
518     LIMIT 10 OFFSET 11;
519
520 To 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.
521
522 =over
523
524 =item 1. Create a Schema object representing the database you are working with:
525
526         my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
527
528 =item 2. Call the B<search> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to limit data on:
529
530         my $limited_posts = $schema->resultset('User')->search(
531           { },
532           { columns  => [qw/me.id me.username/],
533             order_by => { '-desc' => ['user.dob'] },
534             rows     => 10,
535             page     => 2,
536           }
537         );
538
539 This will return exactly 10 row objects, sorted by descending date of birth of the users, starting at the 11th row of the sorted result.
540
541 =back
542