Notes and updates based on riba's most recent comments
[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 (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. 
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 retrieving all columns of the given B<ResultSource> without conditions:
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 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 B<find> will always pull all the columns for the found row, so use the B<search> method for this.
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
226 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 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>.
227     
228
229 =head2 SELECT with aggregates
230
231     SELECT COUNT(*)
232     FROM users;
233
234 To 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
248 The result is not an object, just a number.
249
250     SELECT SUM(amount)
251     FROM prices;
252
253 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>.
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
268 The result is just a number.
269
270 The 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           { },
282           { columns => { sum_amount => { SUM => 'amount'} } },
283         );
284
285 =back
286
287 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>.
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     
297 To 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           { },
309           { columns => [ qw/id username posts.id posts.title/ ],
310             join => 'posts',
311           }
312         );
313
314 =back
315
316 Here "posts" refers to the name of the L<Relationship|DBIx::Class::Relationship> between the "User" source and the "Post" source.
317
318 To 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
334 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.
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           { },
346           { columns => [ qw/id username/, { post_count => { count => 'posts.id' } } ],
347             join => 'posts',
348             group_by => [qw/id username/],
349           }
350         );
351
352 =back
353
354 Here "posts" refers to the name of the L<Relationship|DBIx::Class::Relationship> between the "User" source and the "Post" source.
355
356 The 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
362 Note: Remember to disambiguate the columns when joining two tables with identical column names.
363
364 =begin comment
365
366 Commented out section as ordering by a related source does not work yet. Fix in progress, will comment back in when DBIC is updated.
367
368     =head2 SELECT with simple ORDER BY
369
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;
374
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
376
377     =over
378
379     =item 1. Create a Schema object representing the database you are working with:
380
381             my $schema = MyDatabase::Schema->connect('dbi:SQLite:my.db');
382
383     =item 2. Call the B<search> method on the resultset of the L<ResultSource|DBIx::Class::ResultSource> you wish to sort data on:
384
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             );
392
393     =back
394
395     Here "posts" refers to the name of the L<Relationship|DBIx::Class::Relationship> between the "User" source and the "Post" source.
396
397     The results will be ordered by username, then post title, ready for outputting.
398
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
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
416     GROUP BY users.id, username, dob
417     HAVING count(posts.id) = 1
418
419 To 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           { },
431           { 'columns' => [ qw/me.id me.username me.dob/ ],
432             join => 'posts',
433             group_by => [qw/me.id me.username me.dob/],
434             having   => [{ 'posts.id' => 1 }],
435           }
436         );
437
438 =back
439
440 Here "posts" refers to the name of the L<Relationship|DBIx::Class::Relationship> between the "User" source and the "Post" source.
441
442 The results will be filtered by the HAVING clause.
443
444 =head2 SELECT with DISTINCT
445 ^^ you may or may not want to mention the distinct => 1 flag, which is an automatic "group by the selection" thing. 
446
447     SELECT DISTINCT(posts.title)
448     FROM posts
449
450 To produce DISTINCT clauses, we need to use a hashref argument to the list of items passed to the B<columns> attribute.
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
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:
459
460         my $distinct_posts = $schema->resultset('Post')->search(
461           { },
462           { columns => [{ 'd_title' => { distinct => 'me.title' } }],
463           }
464         );
465
466 =back
467
468 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.
469
470 So 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
488 The result will be an arrayref of the actual values. If a ResultSet object is needed for further refinement, use B<func_rs> instead.
489
490 =head2 SELECT ... FOR UPDATE
491
492     SELECT users.id, users.username, users.dob
493     FROM users
494     FOR UPDATE
495
496 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>.
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
515 The resultset and rows will be returned as normal, and can be used to update the rows without worrying about other 
516
517 =head2 SELECT with LIMIT and OFFSET
518
519     SELECT users.id, users.username
520     FROM users
521     ORDER BY user.dob DESC
522     LIMIT 10 OFFSET 11;
523
524 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.
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,
539             page     => 2,
540           }
541         );
542
543 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.
544
545 =back
546