By Developers, For Developers
PDF Pg | Paper Pg | Type | Description | Fixed on | Comments |
---|---|---|---|---|---|
10 | TYPO | It’s “impedance”, not “impedence”. | 2010-01-16 | Thanks! Fixed in next Beta update. | |
38 | SUGGEST | You state that “Path enumeration allows you to sort a set of rows easily by their hierarchy, as long as the elements between the separator are of consistent length.” The “consistent length” part sound suprisingly innocuous despite being a serious problem that’s even been discussed before in the Jaywalking pattern. I think this point deserves a little more discussion. | 2010-01-27 | Thanks, I have added a footnote warning of this. | |
26 | ERROR | Second sentence in first paragraph of section 2.5: The BugsProducts table implements a many-to-many relationship between bugs and products, not a many-to-one. | 2010-01-16 | ||
42 | TYPO | In the code for CREATE TABLE TreePaths, the second FOREIGN KEY declaration should be FOREIGN KEY (descendant). | 2010-01-16 | ||
22 | SUGGEST | Why do you use one example in the Antipattern introduction and a totally different in the discussion section? I would go for using the storing customers example in both cases. | 2010-02-01 | Good idea! I've reworked the code examples throughout the chapter. Please double-check my work in case I've missed any details. | |
27 | SUGGEST | wouldn’t SELECT product_id, COUNT (*) AS bugs_per_product FROM BugsProducts be more sensible ? | 2010-01-16 | LIMIT syntax is not standard SQL. It's supported only by MySQL, PostgreSQL, and SQLite.
\n
\nORDER BY | |
45 | ERROR | The table of ancestors and descendants does not include (4, 4). | 2010-01-27 | ||
19 | ERROR | fig 1.2 there are three crows feet between bugs and accounts. if this is not in error it should be explained. | 2010-01-27 | Thanks I have added a sentence to explain. | |
19 | ERROR | fig 2. the connection between accounts and comments does not have perpendicular lines on the accounts end. if this is not a typo it should be explained. | 2010-01-27 | Thanks I have fixed the diagram. | |
27 | OK | 2.4 example: a list of email addresses entered by the application, and sent directly to the mail program. any “smart” interpretation on your may break mail, e.g. if a mailing list format changes. | 2010-02-22 | I'm sorry I'm not sure I understand... are you suggesting a mailing list is a legitimate use of a comma-separated list? Or a liability? | |
134 | TYPO | “their respective pseuodocolumn or method” in Choose a random row using an offset | 2010-01-27 | ||
171 | ERROR | In Oracle, this query will ALWAYS return zero (0) rows: “SELECT * FROM Bugs WHERE ROWNUM = :offset”, unless :offset happens to be exactly 1. You could do this with a subquery, but Oracle provides a much superior and more efficient method of getting a random sample from the table - invoked with the SAMPLE keyword, e.g. “SELECT * FROM Bugs SAMPLE (1)” will randomly pick approximately 1% of the rows from the table. I’ll often use something like this to get a single random row from a table: “SELECT * FROM (SELECT * FROM Bugs SAMPLE (1) ORDER BY DBMS_RANDOM.VALUE) WHERE ROWNUM = 1” - this (a) gets a random sample of approx. 1% of the rows from the table; then (b) sorts them in random order; then © gets first first row. | 2010-01-27 | Thanks for the detailed help. I've reworked the Oracle example to a subquery, and I've mentioned the SAMPLE clause. | |
33 | SUGGEST | I dispute that the “Naive Tree” anti-pattern is really an anti-pattern; Oracle supports hierarchical queries based exactly on the type of data model you describe, making it a piece of cake to get all the descendants or ancestors of any given node. In addition, I believe that DB2 and SQL Server 2005 (as well as Oracle 11gR2) also support the ANSI SQL recursive query syntax. Just because something is difficult in some DBMS’s doesn’t make it a bad thing - it may just be a limitation of those DBMS’s. | 2010-01-27 | Thanks, your points are well-taken. I cover recursive queries in the "Legitimate Uses" section of the chapter. PostgreSQL 8.4 also supports recursive queries. But realistically, MySQL and Oracle 10g (and earlier) are hugely popular. Together these two brands may comprise a majority of all RDBMS deployments. Using Adjacency List without considering other designs is a naive approach. | |
166 | DEFER | Where is the row .vs set based operations chapter? Love your existing chapters but find the most frequent AppDev antipattern to be row based operations (cursors, while, etc.) in a set based engine. | 2010-01-29 | That's a great idea Ken! But it's a bit late to add another chapter into the outline for this book. | |
84 | ERROR | The rails lines are incorrect. E.g.: :belongs_to => :commentable, :polymorphic => true should be: belongs_to :commentable, :polymorphic => true The same syntax is used for has_many. | 2010-02-10 | ||
56 | SUGGEST | In “obscuring the meaning of the key”, you say that naming something merely ‘id’ is bad because it’s ambiguous. You then justify this by saying “suppose we have this poorly named table…” This is not a good argument. It is instead an argument for naming the table properly. “bugs.bug_id” is redundant; “bugs.id” is fully specified; “d_bi_ejoai.bug_id” is a total kludge that doesn’t reliably show what semantic role the bug_id is even playing. | 2010-02-11 | ||
84 | ERROR | Addendum to rails error: has_many takes a plural, i.e. has_many :comments, :as => :commentable | 2010-02-10 | ||
91 | OK | “Multi-column attributes” should come before “jaywalking”. 1-M relations are conceptually simpler than, and primitive to, M-M relations. | 2010-02-22 | I thought of the Multi-Column Attributes antipattern as kind of close to Metadata Tribbles, so I ordered these two chapters together. | |
152 | OK | First example under ‘using a derived table’ should list example multiple rows, e.g. 3 / 2009-01-01 / 5680 | 2010-02-22 | I'm not sure I understand this suggestion. I do show multiple rows for the query result in that section (chapter Ambiguous Groups, section Solution, sub-section 3). \n \nOkay, I see you clarified in errata #42391. I'll improve the example in the Beta 5. | |
159 | ERROR | The last “how to recognize” quote does not refer to the only method referred to thus far (i.e. sort by rand()); it refers to select where id = rand(…). Put it in correct order - either move the latter up or the former down. | 2010-02-10 | Good point, I have added a cross-reference. | |
213 | TYPO | Input_order should be input_userid | 2010-02-10 | ||
225 | ERROR | You write that mysql lacks windowing. This is true but misleading; it supports limit with offset, which if anything is easier. It deserves a mention. | 2010-02-10 | ||
83 | TYPO | Section 7.3 How to recognize the Antipattern, third bullet: | 2010-02-19 | ||
89 | TYPO | ‘)’ is missing in: FOREIGN KEY (issue_id) REFERENCES Issues(issue_id, | 2010-02-18 | ||
94 | ERROR | #42266 is not fixed | 2010-03-08 | Thanks, I'll take a look and make sure I can test the code example successfully. | |
164 | SUGGEST | #42270: the ‘derived table’ first example fails to show that it can return multiple bugs per date; the example has all dates unique so implies otherwise. And indeed it’s identical to the 2nd example, thus completely failing to be an example of the difference between the two queries. I.e. the 1st should have 2 bugs w/ the same date; the 2nd shouldn’t. | 2010-03-08 | Thanks for the clarification! I'll edit the example to be more clear. | |
67 | SUGGEST | “The only remedy is for you to explicitly lock the Bugs table while you’re checking it, and unlock it after you have finished deleting the account. Any architecture that requires that kind of locking is never going to do well when high concurrency and scalability is required.” This is incorrect and misleading, because it refers only to table locking. For table locking, it is correct. However, most databases (e.g. mysql’s innodb) support row-level locking, which would work well in this scenario and does just fine with concurrency. | 2010-03-08 | It's not incorrect. You have to block concurrent inserts to ensure MAX(bug_id) doesn't result in a race condition. You can't prevent inserts with row-level locking. That is, which row would you lock to prevent a new row from being inserted? You have to lock the table. \n \nAnyway, thanks for the comment. I agree that paragraph was not clear enough. I'll try to edit it to be better, and I'm changing this issue type to "suggestion." | |
142 | TYPO | Index name for the “Solution:” entry has a spurious “4-1” | 2010-03-12 | Can I ask you for some more context for this issue? I use a hypothetical image filename "screenshot1234-1.jpg" in several places, but I don't see one that is spurious. | |
256 | ERROR | The information about Rails’ migrations, and the way the schema metadata is stored in the database, is outdated as of Rails 2.1. See ryandaigle.com/articles/2008/4/2/what-s-new-in-edge-rails-utc-based-migration-versioning and api.rubyonrails.org/classes/ActiveRecord/Migration.html, “About the schema_migrations table”. P.S. Preventing hyperlinks in these comments is bullshit. Use a real spam classifier and don’t just throw away useful information. | 2010-03-08 | Thanks for the links, I have made some edits to make the description of migrations more or less version-independent. | |
142 | TYPO | re. #42426: imagebin.ca/img/iQCiMG.png | 2010-03-12 | Thanks for the screenshot, that helped make the issue clear for me. It looks like you're using Kindle or iPhone platform? I tried to format the word BLOB as code, but perhaps this doesn't work well with the TOC for digital book rendering. \n \nI don't have either of those platforms to test, so I'll ask the publisher's digital rendering experts and find out how to fix it. Thanks for the report. | |
142 | TYPO | #42436: Nope, OSX Preview. Same thing in Adobe Reader, so it’s probably a syntax typo on your end rather than a client issue. | 2010-03-12 | I use Preview and Adobe Reader on OS X as my primary environment, but I have never seen a collapsible TOC like the one you showed. Anyway, I have now removed the formatting in section headings, so I hope the issue goes away. Please let me know if this issue is still present in the next Beta. | |
54 | ERROR | Example articletags.sql | 2010-03-12 | Thanks for catching this. I've committed a fix. | |
54 | OK | Re. primary ID keys vs natural keys: You missed one frequent case, which is that even though there may exist a natural key (which indeed will have a unique index), it can be large, and duplicating this to other tables is wasteful. For instance, I have a table with ~1M rows whose natural key is ‘url’; it’s a varchar. I have another table with ~10B rows that references the former table. Now, I could reference by url instead and drop the autoinc primary key, but it’s way cheaper for me to do so with an int or bigint than with a varchar everywhere… and it’s also a lot easier to type, have easy to perceive by humans as unique, etc. | 2010-03-14 | In "ID Required," the section "Legitimate Uses of the Antipattern," I do mention the advantage of using pseudokeys instead of long natural keys. | |
156 | SUGGEST | Example of queriey that can’t benefit from index include: It’s not good examples. Some databases, for example PostgtreSQL can use functional indexes. | 2010-03-21 | Yes, you could make a functional index for practically any expression. Microsoft, Oracle, DB2, PostgreSQL, and Firebird support expression indexes, or indexes on computed or generated columns. \n \nBut you would have to bake that into the definition of the table or index; you couldn't use an arbitrary expression and have the query benefit from an index, unless you had defined an index for that exact expression. \n \nI'll edit to clarify. | |
0 | SUGGEST | It’d be nice if you covered a few other common issues:
| 2010-03-16 | Thanks. It's not surprising that the set of bad habits and misconceptions is pretty much inexhaustible. \n \nI did have a chapter in the early outline about joins and the widespread attitude that joins must be avoided at any cost. But I felt I had to cut this chapter -- as well as a number of other ideas -- so I could finish the book! | |
277 | TYPO | #42427 is still present. “The Rails tool that runs migrations automaticaly creates a table schema_info in your database, with one integer column version. This number indicates the current version of your database.” See my links from last time. In rails >=2.1, there is a “schema_migrations” table with multiple rows listing every migration number that has been applied. They are applied in a canonical order (the migration number is a timestamp), but not in a necessary order like in <2.1. Migrations can be interleaved, e.g. in the case of multiple developers asynchronously committing migration code; all as-yet-unapplied (= not in the schema_migrations table) migrations are applied in order when executing “rake db:migrate”. I suggest reading the links I gave to understand this in more depth. PS Typo: s/automaticaly/automatically/g | 2010-03-20 | Thanks, I did read the links you gave. I had intended to rewrite that paragraph but didn't get to it. I'm doing the edit now so it will be in the final book. | |
294 | TYPO | The last function on page, “search”, has at least two extra semicolons in the construction of the query object. | 2010-03-20 | ||
291 | ERROR | Your “what’s the matter with Rails” sidebar gets two facts wrong. Paint me a fanboy, and hate Rails as much as you want, but demonstrably false: - “the rails documentation discourages you from defining constraints in the database”. No, it does not. From the pragprog Rails book page 358, in the chapter named ‘Creating Foreign Keys’ this is mentioned: “You can also choose to establish these constraints in your migrations (and we recommend that you do).” The official Guide on migrations then: “Like anything which operates at the application level these cannot guarantee referential integrity and so some people augment them with foreign key constraints.” True, it does not actively encourage the practice, which is a pity, but discouraing it is not. - “See no evil: You can see the SQL only by injecting a logger into the ActiveRecord::Base class.” Wrong, you can see every query right in the development log. This chapter doesn’t really convince me in any case. “You shouldn’t have to pass SQL queries to the model”. Agreed but you might want to! “Should I copy complex model queries to all my controllers, or should I code them once in an abstract controller?” Ehm, in the model? Yeah! That’s it. “If you’re using database fixtures, you’re testing database access, not business logic.” which is why you use stubs. These arguments really don’t hold any value. “You can even have models in your application that have no interaction with a database.” no shit Sherlock! “The model is where you implement business logic for the application, and storing it in a database is an internal implemen- tation detail of a model.” like calling save() on the model? The relevance of this chapter completely escapes me. | 2010-03-26 | Hi Joost, thanks for your feedback. I'll try to edit my sidebar to be more clear. \n \nRegarding foreign keys, http://guides.rubyonrails.org/migrations.html#active-record-and-referential-integrity says: \n"The Active Record way claims that intelligence belongs in your models, not in the database. As such, features such as triggers or foreign key constraints, which push some of that intelligence back into the database, are not heavily used." \n \nHowever, your points are well taken. I don't mean to blame Rails for people misusing databases or Active Record. | |
21 | OK | In figure 1.2, why are there three “many-to-one” connectors between “Bugs” and “Accounts”? | 2010-04-03 | Hi, thanks for your question. In the example, a bug is reported by one person, fixed by another person, and the fix is verified by a third person. This shows a legitimate case of having multiple foreign keys between two tables, because the meaning of each reference is different. | |
30 | TYPO | 3rd paragraph 1st sentence: It appears there is a missing “you” or something similar: | 2010-04-24 | Thanks for the catch Jason, you're right there's a word missing. I'll make sure it's fixed in the final revision. | |
106 | ERROR | First example on page 106 (using tag1 three times with NULLIF) should be corrected to refer to tag1, tag2, and tag3 | 2010-04-24 | Thanks for the catch, D J, you're right that's a mistake. I'll fix this for the final copy. | |
125 | SUGGEST | In the discussion of the FLOAT type, Oracle remains an oddball. “FLOAT”, as I understand it, actually corresponds to “NUMBER” and is stored in BCD with no loss of accuracy due to roundoffs. Oracle has “BINARY_FLOAT” that is almost IEEE 754, but is actually stored in VAX G FLOAT format, I think. | 2010-04-14 | Thanks for the tip! Yes, I did mention Oracle's nonstandard use of FLOAT in the "Legitimate Uses" section of "Rounding Errors." I'm marking your suggestion fixed. | |
60 | TYPO | In third paragraph (excluding SQL examples) of the “Special Scope for Sequences” block, the first sentence reads, “You avoid the race condition, you have to ….” | 2010-05-05 | Thanks Christian, I've fixed this and it'll be in the final copy of the book. | |
40 | OK | Insert “a” between “defines” and “recursive” indicated below in brackets "Some brands of RDBMS support extensions to SQL to support hierarchies stored in the Adjacency List format. The SQL-99 standard defines [ a ] recursive query syntax using the WITH keyword followed by a common | 2010-05-14 | Thanks Murray, I see what you're saying but I think it's a matter of style. Does one "define syntax," or does one "define a syntax"? Both styles seem clear and natural, like "define behavior" versus "define a behavior." Both are acceptable. I talked to my copy editor and we're going to leave this as is. | |
38 | TYPO | First paragraph should be changed from: | 2010-05-19 | This was fixed in the first printing, as of 2010/5/19. | |
71 | TYPO | Second to last paragraph should be changed from: | 2010-05-19 | This was fixed in the first printing, as of 2010/5/19. | |
79 | DEFER | “But keep in mind that experienced database consultants I think a citation is appropriate for this statement. Otherwise, it comes across as anecdotal (or based solely as your experience). | 2010-05-14 | Thanks Eric that's a good suggestion but I'll have to add that in a future revision. We're going to press! | |
233 | ERROR | On the previous page (233), the reset table is created as AccountResetRequest and the insert statement also references it by this name. However, in the first paragraph, you say that the token “must match a row in the PasswordResetRequest table”, but it should be the AccountResetRequest table instead. | 2010-05-19 | This was fixed in the first printing, as of 2010/5/19. \nChanged table name to "PasswordResetRequest" in all cases. | |
32 | ERROR | In the SQL statement Jaywalking/soln/group.sql the Columns are named inconsistently. Either contacts_per_product or accounts_per_product should be renamed as they represent the same column. | 2010-05-21 | This was fixed in the first printing, as of 2010/5/19. | |
218 | ERROR | All tables defining password_hash column use char(64), but at this page it is char(32). | 2010-12-10 | Yes, you're right, thanks. They should all be char(64) for a SHA256 hash. The "token" column on page 232 should be char(32) for an MD5 hash. | |
19 | ERROR | The first select clause on the page under “Querying Products by Account..” is incorrect. It should be: SELECT p.* instead of: SELECT p.* | 2010-12-10 | ||
20 | ERROR | There seems to be a problem in the query that finds the product with the greatest number of accounts in SQL Server 2005. The query is: SELECT c.product_id, c.accounts_per_product Besides the typo (contacts_per_product) should be (accounts_per_product), SQL Server reports the following error here: “Msg 8121, Level 16, State 1, Line 1 | 2010-12-10 | ||
94 | TYPO | Missing word “to”: “Another way to structure this is to create a dependent table for multiple associations from the Bugs table the Accounts table and give this new” Should read: “from the Bugs table to the Accounts table” | 2010-12-10 | ||
99 | TYPO | “but his numbers don’t adding up” Should be “but his numbers don’t add up” or “but his numbers aren’t adding up” | 2010-12-10 | ||
131 | OK | “In 1953, this famous chain” … “Today, more than sixty years later” — 2013? :) | 2010-11-07 | Thanks, I apologize for the inconsistency. I wrote "more than sixty years later" because according to the Baskin-Robbins web site, the chain actually started in 1945, but the two partners each owned a set of ice cream stores under separate names. They didn't unify their chain of stores under the Baskin-Robbins name until 1953. \n \nBut all this is trivia. I'm going to say this does not warrant any edits. :-) | |
168 | TYPO | “… that it isn’t standard SQL. and other brands…” Incorrect period. | 2010-12-10 | ||
186 | TYPO | “… database directly. you have to …” Incorrect period. | 2010-12-10 | ||
230 | OK | I don’t understand why the auth-salt.php example program does so much of the work in SQL rather than in PHP. As written, the script performs two database queries for the same row in the database. Why not select salt, password_hash, where account_name = ‘bill’, and do the comparison in PHP? | 2010-11-07 | Thanks, that's a good suggestion. In this case I don't think the cost of two queries would be a deal-breaker, since it's a very simple query that returns a single row. I think your suggestion is fine but for the sake of saving some work for my typesetter I'll leave the example as is. | |
219 | ERROR | There is an error in the auth-salt.php script: when selecting salt, account_name is used; when selecting password_hash, acct_name is used. | 2010-12-10 | ||
221 | ERROR | Do not create a password reset token using only the account name and the current timestamp in reset-request.sql. An attacker invoking a password reset mechanism for a given user on the site will already know the username (they supply it) and can guess the current timestamp. (NTP ensures that the clock at the attacker’s computer will be within one or two seconds of the database’s clock. Even if the database isn’t running NTP, it’ll probably be within a few hundred seconds.) Guessing the token with only a username and timestamp is way too easy; much better sources of entropy must be used. Please look on cwe.mitre.org for flaw 330 for a dozen examples of real problems caused by using insufficient entropy in protocols. (Sorry no URL, pragprog forbids it.) | 2010-12-10 | ||
239 | SUGGEST | set-password-escape.php quotes both the userid and the username before sending them through SQL. I think it’d be kind to readers to tell them that they can’t mix-and-match quoting with query parameters in this instance: the password foo’bar would be run through SHA2 as foo’’bar or foo\\’bar or whatever the database adapter uses, but another program elsewhere in the organization that uses query parameters would never be able to validate users with slightly funny (== probably better) passwords. Maybe it’s too obvious, but I’ve seen dozens of PHP programs out there putting ’’ or \\’ or other such silly things on actual webpages, so there’s legion programmers out there who don’t understand that quoting mangles data. | 2010-05-19 | Thanks for the comment, you're right it's often the case that developers apply escaping twice, or else apply escaping once and then pass the content as a query parameter (which does not require strings be escaped). The code example is correct, since I use escaping once and then interpolate the strings into the SQL query. | |
56 | TYPO | In “Do I Really Need a Primary Key?”: What should you with a duplicate when you find one? should be What should you do with a duplicate when you find one? | 2010-05-19 | This was fixed in the first printing, as of 2010/5/19. | |
234 | OK | Don’t know whether I can say its an error or a suggestion. To my belief I read from the IETF site that the term hacker is some one who do it for good. So according to me the term cracker may be the right thing to say . Check out SQL injection part . " serial computer hacker Albert Gonzalez was indicted Hari K T | 2010-05-19 | I respect the distinction you are describing, but it's reasonably common for people to use the term "hacker" for a criminal who uses computer systems illicitly. There was even a film in 1995 called "Hackers". | |
192 | ERROR | The query presented as an antipattern does not produce a cartesian product. It should produce the results intended (12 fixed bugs, 7 open bugs). It’s true that the two Bugs table references have no constraints against each-other, but they do constrain to one row in the BugsProducts Table. I’ve tried the query and get the intended results. | 2010-12-10 | ||
139 | SUGGEST | “.. so the rows where the month is equal to 4 are scattered through the table.” | 2010-12-10 | Thanks, that's a good point. | |
20 | ERROR | The following query is badly broken: The ‘max’ in the HAVING clause causes an arbitrary row of the table to be compared with the maximum. It’s very possible that this query returns the empty set. Here’s the best I could do: | 2010-12-10 | ||
167 | TYPO | Missing comma after “latest” and needless comma after “bug_id_list”. In code file group-concat-mysql.sql: SELECT product_id, MAX (date_reported) AS latest should be: SELECT product_id, MAX (date_reported) AS latest, In code file group-concat-pgsql.sql SELECT product_id, MAX (date_reported) AS latest should be: SELECT product_id, MAX (date_reported) AS latest, | 2010-12-10 | ||
296 | TYPO | Incorrect article in the last paragraph: …as an quick reference… Should change to: …as a quick reference… | 2010-12-10 | ||
18 | TYPO | In code file Jaywalking/soln/create.sql: INSERT INTO Contacts (product_id, accont_id) Should change to: INSERT INTO Contacts (product_id, account_id) | 2010-12-10 | ||
125 | TYPO | In | 2012-02-23 | ||
259 | TYPO | In the text after the heading “Object-relational mapping:”, first sentence, you have used the word “an” when you should have used “a”. It currently says: … as part of an layer of … It should say: … as part of a layer of … | 2012-02-23 | ||
- | SUGGEST | Creator metadata is invalid in EPUB version. File content.opf, element /package/metadata/dc:creator looks like this: The namespace is not valid here. It should be …purl.org/dc/elements/1.1/. E-book readers that expect valid namespace will not show author information. (urls were modified to bypass spam filter) | 2012-02-23 | ||
269 | ERROR | In the PHP source code on the bottom of the page (“Magic-Beans/anti/crud.php”), I think the second line $bugTable->find(1234); should probably read $bug = $bugTable->find(1234); instead. | 2012-02-23 | ||
270 | ERROR | The first line of the source code on this page is $bug->description = $_POST[“summary”]; but should probably be $bug->description = $_POST[“description”]; instead? | 2012-02-23 | ||
98 | TYPO | Sometimes you need to make the result of a query against multiple parent tables appear is if you had stored the parents in a single table => “is if” should be “as if” | 2012-02-23 | ||
295 | ERROR | Appendix A: Fifth Normal Form The BugsAssigned table, under Normalization/5NF-normal.sql, there is a lingering foreign key reference. I think the last FK reference should not be in that table? | 2012-02-23 | ||
196 | TYPO | 12 should be 11 as mentioned on page 206 | 2012-02-23 | ||
149 | OK | FEAR OF UNKNOWN - INTRO- CHAPTER 14 (ITALIAN VERSION OF P1 ? ) : AT LEAST IN ORACLE IT IS NOT TRUE THAT , AS YOU STATED, IN THE QUERY “SELECT first_name || ’ ’ || middle_initial || ’ ’ || last_name AS full_name | 2012-02-23 | ||
72 | ERROR | There’s an error in the SQL statement for the “Issues” view: CREATE VIEW Issues AS results in an error, since the tables have different columns; the query should select only the common columns instead. | 2012-02-23 | ||
90 | OK | There’s an error in the DDL statement for the “Bugs” table: CREATE TABLE Bugs ( This should rather be … Please note that this error appears only in the ePub version, NOT in the PDF version. | 2012-02-23 | Book builder reports: "I did not see this problem in the original epub, viewed in iBooks, and I did not see the problem in the new epub, viewed on a Nook Color." \nSo it seems it has been fixed. | |
168 | DEFER | The book recommends to use separate queries for achieving the goal (“Follow the Single-Value Rule to avoid ambiguous query result”). Suggestion: Please mention that you can also use windowing functions for this kind of problems (e.g. in Oracle | Using windowing functions is a valid solution, thanks for the suggestion. But unfortunately space restrictions prevent me from adding another solution to the chapter. | ||
181 | TYPO | There’s a typo in the statement for the BugsText index: CREATE INDEX BugsText ON Bugs(summary) INDEXTYPE IS The name of the schema is CTXSYS, not CTSSYS (the error appears also in the other statements in this section). | 2012-02-23 | Thanks for the correction. | |
181 | TYPO | The book states (in the section on Oracle fulltext indices): “The index doesn’t stay consistent with changes to data, so you have to rebuild the index manually or on a schedule.” If you create the fulltext index with “PARAMETERS (‘SYNC (ON | 2012-02-23 | Thanks for the tip! | |
243 | ERROR | In sidebar “Are Integers a Non-Renewable Resource?” The math is wrong; a 32-bit integer would last 136 years at a rate of 1 insert per second, not 1000 per second. | 2012-02-23 | ||
413 | ERROR | Appendix 1.1; Figure 26 (Third Normal Form). Table Accounts has tuple {Moe, moe@example.com} repeated. A typo, but technically violates PK {account_id}; not a relation … | |||
27 | ERROR | Column ‘author’ is FK with NOT NULL referencing Account table. In code file Trees/anti/insert.sql: INSERT INTO Comments (bug_id, parent_id, author, comment) VALUES (1234, 7, 'Kukla', 'Thanks!'); may be: INSERT INTO Comments (bug_id, parent_id, author, comment_date, comment) VALUES (1234, 7, 12, CURRENT_TIMESTAMP, 'Thanks!'); | |||
32 | ERROR | Ditto with p.27. In code file Trees/soln/path-enum/insert.sql: INSERT INTO Comments (author, comment) VALUES ('Ollie', 'Good job!'); may be: INSERT INTO Comments (bug_id, author, comment_date, comment) VALUES (1234, 23, CURRENT_TIMESTAMP, 'Good Job!'); | |||
35 | ERROR | Column ‘author’ is FK with NOT NULL referencing Accounts table. In code file Trees/soln/nested-sets/insert.sql: INSERT INTO Comment (nsleft, nsright, author, comment) VALUES (8, 9, 'Fran', 'Me too!'); may be: INSERT INTO Comments (nsleft, nsright, bug_id, author, comment_date, comment) VALUES (8, 9, 1234, 12, CURRENT_TIMESTAMP, 'Me too!'); | |||
15 | SUGGEST | By default, MySQL does not allow ‘||’ as string concatenation operator but works like ‘Logical OR’ operator. So MySQL users should use CONCAT function or run “set @@session.sql_mode=‘PIPES_AS_CONCAT’;” | |||
32 | ERROR | MySQL causes ERROR 1093 (HY000) on UPDATE when running Trees/soln/path-enum/insert.sql. mysql> UPDATE Comments -> SET path = (SELECT path FROM Comments WHERE comment_id = 7) -> || LAST_INSERT_ID() || '/' -> WHERE comment_id = LAST_INSERT_ID(); ERROR 1093 (HY000): You can't specify target table 'Comments' for update in FROM clause solution may be: UPDATE Comments SET path = (SELECT x.path FROM (SELECT path FROM Comments WHERE comment_id = 7) AS x) || LAST_INSERT_ID() || '/' WHERE comment_id = LAST_INSERT_ID(); Is this solution suitable for this book? | |||
33 | TYPO | TYPO in table name. Should be ‘Comments’. TYPOs are in | |||
106 | TYPO | Missing comma after “resolution VARCHAR (1000)”. In code file Metadata-Tribbles/soln/separate-fixed-length.sql: CREATE TABLE BugDescriptions ( bug_id BIGINT UNSIGNED PRIMARY KEY, description VARCHAR(1000), -- variable length data type resolution VARCHAR(1000) -- variable length data type FOREIGN KEY (bug_id) REFERENCES Bugs(bug_id) ); should be: CREATE TABLE BugDescriptions ( bug_id BIGINT UNSIGNED PRIMARY KEY, description VARCHAR(1000), -- variable length data type resolution VARCHAR(1000), -- variable length data type FOREIGN KEY (bug_id) REFERENCES Bugs(bug_id) ); | |||
128 | TYPO | Missing comma after “account_id SERIAL PRIMARY KEY”. In code file Phantom-Files/anti/create-accounts.sql: CREATE TABLE Accounts ( account_id SERIAL PRIMARY KEY account_name VARCHAR(20), portrait_image BLOB ); should be: CREATE TABLE Accounts ( account_id SERIAL PRIMARY KEY, account_name VARCHAR(20), portrait_image BLOB ); | |||
246 | TYPO | Wrong variable name ‘$dbh’ In code file See-No-Evil/anti/no-check.php: ... ② $stmt = $dbh->prepare($sql); ... may be: ... ② $stmt = $pdo->prepare($sql); ... | |||
34 | ERROR | First query reports depth = 4 and second query reports depth = 3 In code file Trees/soln/nested-sets/depth.sql: == DELETE FROM Comment WHERE comment_id = 6; — Reports depth = 2 may be: == DELETE FROM Comments WHERE comment_id = 6; — Reports depth = 3 | |||
35 | ERROR | For immediate parent search, parent.sql in p.35 returns empty set. In code file Trees/soln/nested-sets/parent.sql: SELECT parent.* FROM Comment AS c JOIN Comment AS parent ON c.nsleft BETWEEN parent.nsleft AND parent.nsright LEFT OUTER JOIN Comment AS in_between ON c.nsleft BETWEEN in_between.nsleft AND in_between.nsright AND in_between.nsleft BETWEEN parent.nsleft AND parent.nsright WHERE c.comment_id = 6 AND in_between.comment_id IS NULL; may be: SELECT parent.* FROM Comments AS c JOIN Comments AS parent ON parent.nsleft < c.nsleft AND c.nsleft < parent.nsright LEFT OUTER JOIN Comments AS in_between ON in_between.nsleft < c.nsleft AND c.nsleft < in_between.nsright AND parent.nsleft < in_between.nsleft AND in_between.nsleft < parent.nsright WHERE c.comment_id = 6 AND in_between.comment_id IS NULL; | |||
20 | TYPO | Jaywalking/soln/group.sql uses c.contacts_per_product in the outer query, but the inner query names accounts_per_product. | |||
35 | ERROR | Tree/soln/nested-sets/insert.sql shows a UPDATE statement to insert a new leaf node below comment #5 which results in NS values 8 and 9 being spaced into the series of NS values. So everything above 8 would have +2 added to it. The where clause in this statement is WHERE nsright >= 7, which seems incorrect to me. I believe it should be WHERE nsright >=8 This would not cause any issue if it were executed at this node of the tree, but would cause an issue if the leaf is being added to a non-leaf node. For example, if the update was run to add a leaf to comment #6, it would create an error. Comment #6 has nsleft=9 and nsright=12, and it’s child is nsleft=10, nsright=11. If you added a new node, the new node would have values nsleft=12, nsright=13. If you followed the example, and used WHERE nsright >= 11, then you would update the other child’s nsright from 11 to 13, and the new node would also have an nsright=13 which is a duplicate number. | |||
193 | TYPO | The sentence “Each of the twelve rows for FIXED bugs is paired with all seven rows for OPEN bugs.” in the chapter “Antipattern: Solve a Complex Problem in One Step” should be “Each of the eleven rows for FIXED bugs is paired with all seven rows for OPEN bugs.” | |||
2 | ERROR | The author refers to Lisp and Haskell as “declarative languages”. That isn’t actually true. In some situations, they can be more declarative than (say) C or Python, but they are nowhere near as declarative as SQL. A declarative language is one in which you state what you want the program to compute but not how to compute it. In Lisp and Haskell, as in most programming languages, you must do both. | |||
190 | TYPO | 1. In the phrase: “Enita non sunt multiplicanda praeter necessitatem” it should be “Entia”, not the “Enita” |