这不是将它们添加到您的数据库中;而是将它们添加到您的数据库中。这只是数据操作语言中的 WHERE 子句中的引用列,而不是数据定义语言。添加它们,以便它们在数据库/服务器级别运行,意味着按照链接的文章在 DDL 中声明它们。然后MySQL将阻止将一行插入到父PK不存在的子表(FK)中。这就是引用完整性。如果在DDL中声明,则为声明性引用完整性。
数据模型已更新,现在可用于第五个审查。您没有说明您的喜好,... date vs ... dtm。我正在使用后者,因为它更加熟悉,也可以识别时间组成部分。易于改变。
这个答案已达到最大长度。继续在“第二部分”
Part I
Revised 09 Dec 10 01:00 EST
Looked at your DDL. Ok. We need to take a step back and organise your database first. That will solve half your problems (your SQL will be straight-forward; and fast; less indices; no temp tables required). For a while I thought, aha, you have your columns, it must be stable, but there is no chance. Top down from scratch, ok. Have a look at this Entity Relation Diagram (no use working on the Data Model, which is Entities, Relations and Attributes, until we get the ERs right), and check that it is correct.
The way to do that is, answer the following questions (short answers are fine). These questions are clarifying the Entities and Business Rules. How you understand databases in general, and your data in particular is crucial. You have come a long way, on your own, so we can take it from there.
I think ▶this post◀ might be helpful to you, in order to understand the formal stages that should be followed; which we are short-circuiting here.
Most important, totally, and completely, forget about the function and any coding requirements. Data has to be modelled independent of the application, simply as Data. Function Modelling is a different science. First get one right; then get the other right; and the two together play beautiful tunes. Try jamming them together; doing both tasks at the same time, and they won't even make a suburban garage band.
For brevity, and the sake of anyone reading this, I with use a Closed and Open Section; when an Open item (discussion) is closed, I will make it concise, and move it to the Closed section. Maintain the numbering, because things sometimes come back to haunt us. You may wish to do the same, or even delete the discussion on your side.
The links for the pretty pictures are at the end.
Apologies: the editing does not work; sub-numbering is inconsistent
Closed Issues
users.bb_locations_csv is a many-to-many relation between users and locations:
Each of those elements should be an entry in a discrete column, in a discrete row
One users can have many locations and 1 location can have many users is many-to-many
Read ▶this post◀ for a discussion of how that is treated and what stage it is dealt with
At this Logical Stage, that is just a n::n relation, as I have drawn, you can forget about it for now, it will be supplied, simply, when we get to the physical Stage.
Trust me, I will provide code that in no more complex than ...WHERE IN () for your declared purpose.
On second thought, if I break your fingers, you will type even slower, so I better not
Ok, your app is browser based, and the page is dynamic (my advice was for static pages that need to be touched up); go ahead with check boxes. .
users.bb_categories_csv is many-to-many relation between users and categories
Ditto. .
Confirmed: a bulletin (bbs) does not exist without an user; an user issues a bulletin, and that starts the whole cycle; then invites replies and ratings.
3.1 Confirmed: There is really only one bulletin board and it does not exist as a Thing in the database.
3.2 Confirmed: that the org will never have more than one bulletin board, and the classifications and categorisations are all adequately handled by the Category table/function
Deleted.
Confirmed: The difference between bulletins and replies is that replies are dependent on a bulletin to exist, they do not have a title and they are not categorised by location or category because they are dependent on the bulletin itself to exist.
Deleted.
Comments noted. Resolved.
7.1. For each single bulletin submitted by another user, each user can post more than one reply.
7.2. For each single bulletin submitted by an user, that user can post one, or more than one reply.
7.3. Deleted.
7.4. Deleted.
The Data Model now allows more than one reply per user per bulletin; including the User who submitted the bulletin.
. 8. Confirmed: each user can post at most one rating to a bulletin (which can be revoked/changed) . 9. Confirmed: each user can post at most one rating to a reply (ditto)
10.1. Given: username comes from the organisation and is the unique name that identifies employees. For example emails are [email protected] - authentication is done with ldap and this is required in order to connect an retrieve other information about the employees
Confirmed: UserName is an excellent Identifier
10.2. Confirmed: FirstName, LastName ... BirthPlace, etc remain as (the traditional) columns for ensuring People are not duplicated. . 11. Given: At the moment we can Identify our offices by casual names which are generally know within the organisation, since we only have about 3 main offices and many field offices. So examples would be Washington DC or virginia field office. In total I think we will try and keep the total below 20. I want to record the exact address of each location as well because that could be used to uniquely identify offices to users.
Provided: StateCode+Town as PK; IsMainOffice as boolean.
. 12. Confirmed: Description and Name for Category are required. . 13. Given:Users will not be able to post to some categories. Only users with sufficiently high rights will have the right to post to certain categories.
Provided: Permission in User, Location, Category is a method of evaluating such rights.
. 14. Confirmed: Location.Administrator is UserId of admin for the Location. . 15. Given: There will only ever be a need for a like or a dislike. I don't think there needs to be a neutral position because this is the same as just not voting? Liking seems more relevant to bulletin replies that posts to be honest. Ie 'i see your response and instead of writing my own I will just agree with you - the existing bulletin board is somewhat of a social aspect in the orgainsation and I think liking and disliking/agreeing and dissagreeing creates a level of controversy that encourages participation. However liking or disliking a bulletin may not always be entirely appropriate.
15.1 Provided: Like as boolean in BulletinRating and ResponseRating. This will require interpretation on every access. 15.2. When it is no longer a boolean, it can be changed to a RatingCode, and implemented as a Lookup table. The names are then determined by Joins, and interpretation is eliminated. I drew this in the First Data Model, so that you could see what I meant 15.3. Removed in the Second Data Model. . 16. Confirmed: each user has a home Location (other than the list of Locations that they are interested in). . 17. Confirmed: Permission as per (13). . 18. Confirmed: Further Permissions may be be required, as per Data Model.
18.1. If you do this now, you won't have to worry about when organisation decides to prevent a certain Person from posting Responses or Bulletins, or Rating them; and wants that feature implemented yesterday.
18.2. Even if you do not implement it, leave gaps between the values you do implement. . 19 Confirmed: a Bulletin is about a Location.
19.1. Confirmed: There are no Bulletins without a Location
19.2. Confirmed: There are no Bulletins without a Location.
19.3 Confirmed: There are no Bulletins without a User (declarative). But so far we have no way of constraining that User; therefore any User can inset a Bulletin for any Location ( you could constrain it in code, eg. to Locations each User Is Interested In.
19.4 Confirmed: There are no BulletinRatings without a Bulletin and a rating User.
19.5 Confirmed: There are no Responses without a Bulletin.
19.4 Confirmed: There are no ResponseRatings without a Response and a rating User.
19.7. But, there can be Users, Locations, andCategories`, independently.
. 20. If you do not mind, I will provide naming conventions, etc. They should be self explanatory, and the value will show up only when you start coding SQL. Please ask, if anything isn't. For starters, all names are singular. Mixed Case is easier to read (you are supposed to use capitals for SQL language).
20.1. My experience is table_name as opposed to tableName are really technie forms, and users do not like them; Consistent mixed case is liked by everyone. It is one of those things that is impossible to change, so choose carefully. . 21. For your need to group tables together, which is good, keep in mind that that is a Physical issue. At the Logical Data Model level, the tables have normal names, uncluttereded by physical issues. Imagine that the physical tables are prefixed with something like (and please use capitals for this): - REF_ for reference (such as User) and lookup tables - BUL_ for Bulletin system . I am not able to name tables with uppercase letters? Im not sure why. I don't know why I can't have uppercase table names. Is it to do with using MyIsam database tables?
The universal convention is that SQL Language is expressed in upper case; every report and admin tool I have ever used generates such SQL code. So we can't use upper case. Lower case or mixed case only. So the choices boil down to table_name or TableName; we need a separator of some kind. For reasons already provided, I strongly recommend mixed case, capiatlised, and not the OO style with the leading letter uncapitalised.
. 22. rank (all) can be derived directly from the database (remember, do not worry about the code during Data Modelling). If you store it, it is a Normalisation error; a duplicated column; which has to be kept up-to-date; which can get out of synch with the derived value; which is called an Update Anomaly. Fifth Normal Form eliminates Update Anomalies. That is my minimum level of Normalisation, so that is what you will get from me.
22.1. I am not interfering with the sort order or popularity issue at all; in fact, by the sounds of it, you haven't closed that functionality. I am only taking redundant data, the rank column, out, as part of the Normalisation process.
22.2. Here's a ▶Quick Tutorial◀ on the RANK() operator (as it is commonly known). It is not ANSI SQL; it is an Oracle and MS extension. However it is not required if you understand Subqueries, which is why Sybase does not have it. I doubt MySQL has it, so you need to get your head around it. Understanding Scalar Subqueries is a pre-requisite. Sybase syntax, so whack your semi-colons in, etc. Feel free to ask specific questions. . I have never seen that approach of writing Rank = (SELECT.... Is that the same as (SELECT ...) as Rank?
I have posted a separate Answer for that.
. 22.3. Needing to understand why, is no problem at all. Only children blindly follow simple rules, and you are certainly not one of them. . 23. Confirmed: users.total_bulletins is redundant; it can be derived. Removed. . 24. All your PKs are Ids. Haven't you gotten tired of getting lost in the code yet ? Forget about sticking Idiot PKs on everything that moves, let's find out How your users Identify their Entities; what Entities are truly Independent, and the other which depend on Independent Entities.
24.1. Never use Id or any such form. Where it is a PK, use the full form.
24.2. Call location_id, location_id, wherever it is, including the PK table. The exception is when you need to show the role. This will become clear in the Data Model. . 25. You have no Declarative Referential Integrity, no Defined Foreign keys. That is bad news for many different reasons. Once these questions are clairified, please add them in. DRI means that as much as possible, if not all, Integrity is Declared in SQL. ISO/IEC/ANSI SQL standard allows for this, but the freeware end of the market does not provide the standard, and is slowly catching up. It means the server will not allow a row in the FK table to be added unless the PK exists in the parent table. MySQL recently provided DRI for Foreign Keys. For FKs, refer to ▶this article◀.
25.1. For CHECK constraints and RULES, you will have to implement those in code.
my foreign keys are like, users-id(fk) = users.id(pk) Im not sure how to add them other that what I have done but will certainly do so once I know how to.
That's not adding them into your db; that's merely referencing columns in a WHERE clause in Data Manipulation Language, not Data Definition Languge. Adding them, so that they function at the db/server level, means declaring them in DDL, as per the linked article. Then MySQL will stop a row from being inserted to a child table (FK) where the parent PK does not exist. That is Referential Integrity. If it is declared in DDL, it is Declarative Referential Integrity.
In addition to enforcement of RI, everyone can see the definition: report tools can be used by the users to access and report from the db, without having to get someone to code a report.
Yes, as far as I know. Confirmed at ▶this site◀. The code I have provided for the subquery uses DRI, so we can test that and get it out of the road early. You have to check for your specific version of MySQL.
Twenty-Five. Comments Noted. I ama not a MySQL specialist. Yes, those are the issues you have to figure out for yourself. In general, from my perusing, MySQL is legless; for anything SQL-ish, you need InnoDB.
But do not let that hold you back. Use Engine=MySQL for now, without the Declarative SQL, and keep going with both the Data Model and the Subquery. Work on InnoDB in the background.
To be clear, the DDL I have provided should work for MyISAM (and "do nothing" in the DRI department, until you get InnoDB).
. 27. Given: I have rethought the sorting requirements for bulletin. Users could sort chronologically- easy,makes sense. Users could sort bulletins by the date of the latest reply to the bulletin. Then we can forget about rank and it should be really easy to sort bulletins chronologially by the time of their last response? What are your thoughts.
Yes. that is sensible and quite common, most people understand chronological order. You will have to mess with the filters they choose in the search window (choose: Location or list; choose: Category or list; choose: My Bulletins or all).
Open Issues
(Nil)
Data Model
Ok, assuming you do not have issues with the ERD, and implementing all Closed Issues, I have modelled the data, and prepared a Fifth Data Model 09 Dec 10 for your review. I definitely need much more feedback, questions, etc, on this. I am experiencing difficulty accepting that it is done. Probably best to start writing real code for your problem areas.
Links
▶Link to IDEF1X Notation◀ You really need to read and understand this, before you read the Data Model.
The Keys are pretty much straight IDEF1X (except for UserId which I provided as a counterpoint); which means purse Relational Keys. Un-enhanced and not optimised for Physical considerations. Before you baulk at them, first notice them, register them, and evaluate them. Of course we can addIdiot keys, but before we do that, let's make sure we understand what we are going to lose.
Notice the Identifiers (solid lines) as per the Notation document. The spine, the vertebrae of the system is Location ... Bulletin ... Response.
Notice that Keys actually implement many Business Rules.
Notice the Natural Hierarchy that I have rendered. See if there is any meaning in it for you.
The VerbPhrases are really important; see if they mean anything.
Comments re First Data Model and Responses
One question I have is that the primary key of the location will be used to form the child primary key?(they are joined by a solid line) I don't really understand that concept
Yes. the PK for Location (above the line) is (StateCode, Town). That PK the two columns together, a compound key, is migrated from Location to Bulletin anyway, as an FK (bold). We are additionally using it to form the Bulletin PK (above the line).
If and when we need a Surrogate key, we will add it. For now, we are working out the Identifiers. So the question to contemplate is:
What is a good Identifier for Bulletin ?, what do your users naturally use to Identify a Bulletin ...
"have you seen the bulletin from Virginia FO yesterday ?",
"Sally from Washington sure writes good bulletins", etc.
or why that relationship does not exist between the user and the bulletin?
Well, that relation cannot exist between User andBulletin, but a relation exists, the dotted line, meaning UserId is an FK in Bulletin(bold), but not used it to form its PK (below the line).
Or do you mean: the User is a strong Identifier for Bulletin (and therefore should be used to form the BulletinPK, therefore the line should be solid) ?
Fine. Excellent. That is what modelling re Identifiers is all about. That clears up an area that I did not like, in that we had non-unique indices. That resolves my issue as well.
As per intention stated further above, since I have now shown Rating as a table and what the rendering would be, once, I shall remove it
I think Permission should be an Entity.
Bulletin PK is now (StateCode, Town, UserId, SequenceNo). To be clear, SequenceNo is within StateCode, Town, UserId: it will be 5 for Sally's 5th bulletin re MO/Billngs FO.
Note that user Settings BulletinsPerPage,etc, are 1::1 with User, so they are in User; child table would be incorrect.
Typographical errors corrected.
Comments re Second Data Model and Responses
The PKs for both Bulletin and Response have been changed to reflect (7). BulletinNo and ResponseNo have been replaced with BulletinDate and ResponseDate (which used to be CreatedDate), in order to allow multiple replies per User per Bulletin.
Comments re Third Data Model and Responses
Trust you had a good break.
At least 30 years ago (that I am aware of), the giants in the industry had this debate. Names are always singular. Tables are nouns. VerbPhrases are verbs. This is not limited to db naming conventions, it applies to documents, theses, dissertations, etc. You may have 5 conclusions at the end of the the doc, but the section or chapter title, in both the ToC and the top of the page is "Conclusion".
After fighting them all the way through Uni, as soon as I started my first paid programming job, and saw the importance of the rules in the real world, as opposed to the theoretical arguments we had in college, I gave it up as a waste of time. All that time and energy I wasted was released to do productive work. Since then, I don't question the giants; I just accept. That their minds are greater than mine. It is like accepting Standards, or behaving within the law, or God. I have no really, really good reasons for doing anything illegal.
Anyway, the ease of languaging (discussion, SQL, documentation) that is supported by such rules cannot be adequately explained; as you write more and more SQL code, it will become clear.
You are always free to use whatever you want. I deliver singular only.
Fine with me.
But you need to keep in mind, those two elements, in the identified sequence (ala non-PK Unique Index, or Alternate Key) are universally required to establish Uniqueness for a Person. Removing them will result in two things. First, you will no longer be able to identify uniqueness across Users (and thus you may have duplicate rows). Second, the AK becomes non-unique, an Inversion Entry.
The point is (contrary to one of the posts), any column that is 1::1 with theUserPK, should reside inUser. All preference settings. Since we cleaned up theInterestedLocationsandInterestedCategories, I know only of onlyBulletinsPerPageremaining; but I am sure there are others. IsPreference2is an eg. of a boolean;NumPreference3is an eg. of an Integer. Etc. You can tell me what the real Preferences are.
(Let's try that in plural: ... any column that is 1::1 with theUsersPK, should reside inUsers. Just doesn't do it for me, I get hung up on the broken English, and I am a bit precious about my mother tongue.)
Data Model Updated.
Excellent. Let me know when you are comfortable with that, and I will give you the Physical Model.
How about the VerbPhrases ?
Comments re 06 Dec 10 20:38 EST (Small Updates)
. 28. Where there is only one occurrence of PK as an FK, of course, the FK column name is the same as the PK column name. However, when there is more than one occ of the FK (take a look at ResponseRating), there are three UserIds), we need to differentiate them. In IDEF1X terminology this is called Roles. The Role of the User who issued the Bulletin is Issuer, and so on. Obviously it is better to use that name, and keep it consistent throughout the hierarchy (not UserId in Bulletin and then when we get to Response, where there are two, and a differentiation is demanded, change it to IssuerId. I thought you might have a problem with that; in the early stages, the usage is Issuer.UserId so that it is absolutely clear the it is UserId as an FK, and the Role is Issuer; when we get to the physical model, it gets simplified to IssuerId.
Likewise, we have many DateTime columns (Date for short if you like; otherwise Dtm), that need to be differentiated. . 29. Did the IDEF1X Notation doc not make sense ?
The PK for each table is above the line, in the specified order.
Remember we are carrying the PKs of the parent tables anyway, and if there is meaning, using those FKs to form the child PK.
For Bulletin:
The Location FK (StateCode, Town) for which it is Issued
To delete all ResponseRatings for this Bulletin, use WHERE = on those four Bulletin columns.
. 30. Because (State, Town) is the PK of Location, carrying wherever. And it forms part of the Bulletin PK, so any dependent tables carry those columns because they are carrying the Bulletin PK.
Look for the coloured Tabs (This version only) . 32. Those are Verb Phrases. The way to read them is detailed in the Notation doc. It appears you have a good handle on it. It is really important to get the table names (and the Verb Phrases) right, because change is difficult after implementation. If you tell me Office is better than Location, that's fine with me.
Read: Office Is Activated By Bulletin
Feel free to supply another Verb Phrase. AFAIC, the Office is dead to the rest of the org, and only comes alive on their radar (is activated by) the issue of a Bulletin. I realise it sounds silly here, but ignore that for a moment, something along the lines of "Office expresses its aliveness; advertises its activity, by issuing a Bulletin".
Have a quiz at Mark's Sensor Data Model, for some nice Verb Phrases.
We had previously identified that (State, Town) is the PK, I will leave that as is Refer to (38) for change.
. 33. Worth discussion. Yes, if you are going to display it when (eg) displaying Responses, and the users understand UserName. No, if it is 30 bytes, and there is also an unique 4 byte UserId. The idea is to make these choices consciously, aware of what you are giving up, when you eventually decide that some 6 column 30-byte key is too cumbersome to migrate to the children.
I did state at the outset, I would use UserId as a typical Id Pk, because it is carried/migrated to several child tables.
We can leave how that is created for later. But it is a pure Surrogate PK.
. 34. No problem. Category already has it. I'll change Order to ListOrder.
. 35. Sure. Based on what I have read and heard, I am quite happy with it. But I would like more back-and-forth to achieve some confidence, before you write code. Alternately, view it as a learning experience, and accept that the model and code may change later. Would you like me to produce the Physical now ? If you give me any and all corrections, I will publish the next version. I am expecting preferences in User. Also, quickly run through the functions and check that you have all the columns you need.
Do look at some of the other answers, for the purpose of learning, and interest.
. 36. Joins. You just join on four three columns as opposed to one. SQL is cumbersome with joins, and the new syntax which was supposed to make it easier, is actually more cumbersome. My coders never write joins: we save time and typos. I have a proc that given two or more tables, will generate the code with all the columns and joins. I don't know enough of MySQL to convert that for you.
Data Model Updated. .
Comments re 08 Dec 10 20:49, Fourth Data Model and Responses
. Check the previous section immediately above, there are small updates.
IDEF1X: Your speed is fine.
Note the child always "inherits" the Parent PK, as an FK (either solid or broken line), otherwise there is no Relation between them. By using these columns that exist in the child anyway, to form the child PK, we carry the meaning (and that is the difference between solid and broken). And thus we do not need to look for an independent Identifier for the child. The Relational power in this method will become clear later, when you are coding.
The section we are dealing with is about Identifiers: natural vs unatural; meaningful vs meaningless. Later you will see how we can use the Relational capability of the engine, when the child PK is formed from the parent PK. (Isn't your surname the same as your father's ?)
It is also important to understand Relational databases and their capability. That is lost when we approach the database (eg) from an OO perspective, and treat it as a location to make our classes "persistent". Therefore, we will try to learn and use Relational terms. It gets difficult when you go to France and expect that they speak American, and use the same currency; learn to speak 10 words of French, and they welcome you with open arms, and you'll have quite a different experience with the locals.
Anyway, go ahead with implementing the model. Just realise we will probably make a change at some point. Save all your DDL. Save all your test data as insert statements or as a table backup or character format export (no idea what MySQL can/cannot do in this area). . 37.1. Handled, the n::n Relation with Office & Category. You will only "see" that when we get to the Physical Model.
37.2. Done.
37.3 Done. . 38. Excellent. Shorter as well. Note they will never be able to have two Offices in the same Zip Code. NUMERIC(5,0) is good, but I thought the US was moving towards 7 digits. Doesn't matter, you can figure it out; it is an excellent PK for Office. Now this column, which was part of Address, probably ZipCode, has been elevated to a higher purpose, without duplication; since we are carrying it in 5 child tables, and we want the PK name to be clear, as per previously explained conventions, we will call it OfficeCode; OfficeZipCode might be silly.
We need an Unique Index on Name to ensure they do not add two Offices with the same name. Note, for explanation purposes, this is is actually the logical key of Office, replacing (StateCode, Town), and it remains so.
I still think you may need StateCode and Town as a quick reference (other than sitting somewhere in Address)
Data Model updated, Fifth now available for review. You did not state your preference, for ...Date vs ...Dtm. I am going with the latter, as it is more spceific, identifying the time component as well. Easy to change.
This Answer has reached maximum length. Continued in "Part II"
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';
-- -----------------------------------------------------
-- Table `users`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `users` (
`id` INT NOT NULL AUTO_INCREMENT ,
`username` VARCHAR(45) NULL ,
`password` VARCHAR(100) NULL ,
`email` VARCHAR(255) NULL ,
`first_name` VARCHAR(100) NULL ,
`last_name` VARCHAR(100) NULL ,
`permission` INT NULL ,
`created` DATETIME NULL ,
`modified` DATETIME NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `categories`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `categories` (
`id` INT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(45) NULL ,
`description` TEXT NULL ,
`order` INT NULL ,
`admin` INT NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `locations`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `locations` (
`id` INT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(45) NULL ,
`description` TEXT NULL ,
`address` TEXT NULL ,
`order` INT NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `posts`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `posts` (
`id` INT NOT NULL AUTO_INCREMENT ,
`post_id` INT NOT NULL ,
`user_id` INT NOT NULL ,
`category_id` INT NOT NULL ,
`location_id` INT NOT NULL ,
`title` VARCHAR(45) NULL ,
`content` TEXT NULL ,
`created` DATETIME NULL ,
`modified` DATETIME NULL ,
PRIMARY KEY (`id`, `post_id`, `user_id`, `category_id`, `location_id`) ,
INDEX `fk_posts_users` (`user_id` ASC) ,
INDEX `fk_posts_posts1` (`post_id` ASC) ,
INDEX `fk_posts_categories1` (`category_id` ASC) ,
INDEX `fk_posts_locations1` (`location_id` ASC) ,
CONSTRAINT `fk_posts_users`
FOREIGN KEY (`user_id` )
REFERENCES `users` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_posts_posts1`
FOREIGN KEY (`post_id` )
REFERENCES `posts` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_posts_categories1`
FOREIGN KEY (`category_id` )
REFERENCES `categories` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_posts_locations1`
FOREIGN KEY (`location_id` )
REFERENCES `locations` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `likes`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `likes` (
`id` INT NOT NULL AUTO_INCREMENT ,
`user_id` INT NOT NULL ,
`post_id` INT NOT NULL ,
`like` TINYINT(1) NULL ,
PRIMARY KEY (`id`, `user_id`, `post_id`) ,
INDEX `fk_posts_users_users1` (`user_id` ASC) ,
INDEX `fk_posts_users_posts1` (`post_id` ASC) ,
CONSTRAINT `fk_posts_users_users1`
FOREIGN KEY (`user_id` )
REFERENCES `users` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_posts_users_posts1`
FOREIGN KEY (`post_id` )
REFERENCES `posts` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `sort_options`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `sort_options` (
`id` INT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(45) NULL ,
`description` TEXT NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `preferences`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `preferences` (
`id` INT NOT NULL AUTO_INCREMENT ,
`user_id` INT NOT NULL ,
`pagination` INT NULL ,
`sort_option_id` INT NOT NULL ,
`categories_csv` VARCHAR(45) NULL ,
`locations_csv` VARCHAR(45) NULL ,
PRIMARY KEY (`id`, `user_id`, `sort_option_id`) ,
INDEX `fk_preferences_users1` (`user_id` ASC) ,
INDEX `fk_preferences_sort_options1` (`sort_option_id` ASC) ,
CONSTRAINT `fk_preferences_users1`
FOREIGN KEY (`user_id` )
REFERENCES `users` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_preferences_sort_options1`
FOREIGN KEY (`sort_option_id` )
REFERENCES `sort_options` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
They key to having an efficient database is to simplify. The main goal of a relational database is not to repeat any information. I took your SQL dump and quickly drafted a simpler version that is normalized, to the best of my knowledge. I did leave some of the fields you had in for cvs's ect. I have removed fields that it would be simpler to just recalculate by querying the db when the information is needed, such as a users total posts and a ranking of a given post. I also removed your bb_replies as you can accomplish the same result with referencing to a parent post. I have renamed the tables slightly to what made sense to me, you can use what ever naming scheme you feel comfortable with. I find that using terms that are simple makes it easier to understand how the data relates to each other.
I must admit that I do agree with some of the comments above, there are plenty of BBs out there that work just fine and would have all the functionality you are looking for. And you are lucky I am in the reading mood tonight lol that was one long question. Simplification is key in everything :)
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';
-- -----------------------------------------------------
-- Table `users`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `users` (
`id` INT NOT NULL AUTO_INCREMENT ,
`username` VARCHAR(45) NULL ,
`password` VARCHAR(100) NULL ,
`email` VARCHAR(255) NULL ,
`first_name` VARCHAR(100) NULL ,
`last_name` VARCHAR(100) NULL ,
`permission` INT NULL ,
`created` DATETIME NULL ,
`modified` DATETIME NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `categories`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `categories` (
`id` INT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(45) NULL ,
`description` TEXT NULL ,
`order` INT NULL ,
`admin` INT NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `locations`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `locations` (
`id` INT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(45) NULL ,
`description` TEXT NULL ,
`address` TEXT NULL ,
`order` INT NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `posts`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `posts` (
`id` INT NOT NULL AUTO_INCREMENT ,
`post_id` INT NOT NULL ,
`user_id` INT NOT NULL ,
`category_id` INT NOT NULL ,
`location_id` INT NOT NULL ,
`title` VARCHAR(45) NULL ,
`content` TEXT NULL ,
`created` DATETIME NULL ,
`modified` DATETIME NULL ,
PRIMARY KEY (`id`, `post_id`, `user_id`, `category_id`, `location_id`) ,
INDEX `fk_posts_users` (`user_id` ASC) ,
INDEX `fk_posts_posts1` (`post_id` ASC) ,
INDEX `fk_posts_categories1` (`category_id` ASC) ,
INDEX `fk_posts_locations1` (`location_id` ASC) ,
CONSTRAINT `fk_posts_users`
FOREIGN KEY (`user_id` )
REFERENCES `users` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_posts_posts1`
FOREIGN KEY (`post_id` )
REFERENCES `posts` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_posts_categories1`
FOREIGN KEY (`category_id` )
REFERENCES `categories` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_posts_locations1`
FOREIGN KEY (`location_id` )
REFERENCES `locations` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `likes`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `likes` (
`id` INT NOT NULL AUTO_INCREMENT ,
`user_id` INT NOT NULL ,
`post_id` INT NOT NULL ,
`like` TINYINT(1) NULL ,
PRIMARY KEY (`id`, `user_id`, `post_id`) ,
INDEX `fk_posts_users_users1` (`user_id` ASC) ,
INDEX `fk_posts_users_posts1` (`post_id` ASC) ,
CONSTRAINT `fk_posts_users_users1`
FOREIGN KEY (`user_id` )
REFERENCES `users` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_posts_users_posts1`
FOREIGN KEY (`post_id` )
REFERENCES `posts` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `sort_options`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `sort_options` (
`id` INT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(45) NULL ,
`description` TEXT NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `preferences`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `preferences` (
`id` INT NOT NULL AUTO_INCREMENT ,
`user_id` INT NOT NULL ,
`pagination` INT NULL ,
`sort_option_id` INT NOT NULL ,
`categories_csv` VARCHAR(45) NULL ,
`locations_csv` VARCHAR(45) NULL ,
PRIMARY KEY (`id`, `user_id`, `sort_option_id`) ,
INDEX `fk_preferences_users1` (`user_id` ASC) ,
INDEX `fk_preferences_sort_options1` (`sort_option_id` ASC) ,
CONSTRAINT `fk_preferences_users1`
FOREIGN KEY (`user_id` )
REFERENCES `users` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_preferences_sort_options1`
FOREIGN KEY (`sort_option_id` )
REFERENCES `sort_options` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
如果我没有说明顺序,MySql 将以从 StudentCourse 获取的任何顺序生成结果集(按时间顺序?;按索引?)。无论默认顺序是什么,找出它,您需要知道它,从而在不必要时避免使用 ORDER BY。
将 ORDER BY 拿出来玩玩。
尝试 ORDER BY 4 DESC, 1
这不是“通过”,我是在一个 SQL 命令中告诉它如何处理我的结果集。您所做的唯一传递是在您的应用程序(PHP?)和 MySQL 之间。
2.1.好的,当您完成(2)并且非常高兴您理解它时,请做这个练习。
SELECT (SELECT Name
FROM Course
WHERE CourseId = sc.CourseId
) AS CourseName,
() AS FirstName,
() AS LastName,
Mark
FROM StudentCourse sc
ORDER BY 1, 4 DESC
2.3。在不使用子查询的情况下编写该查询 (3),并确保检查结果。如果您在编写代码时发现自己在笑,这是一个好兆头。只要生成正确的结果集,就可以通过,但请尝试编写最有效的代码(最少的 COUNTS 和 GROUP BY 等)。仅当您想在同事周围转圈,以便能够回答数据库中的任何“我如何编码......”问题时才执行此操作。
我不确定您所说的在不使用子查询的情况下编写该查询是什么意思?我认为我们想避免使用 group by 等
是的。绝对地。你已经向前走了。现在向后走不要绊倒。当使用子查询与连接更好时,这将真正帮助您理解向前行走。使用 GROUP BY 和 COUNT 对查询进行编码。最少的。别笑。
试试这个(我提供 ANSI 标准 SQL;我没有 MySQL;你必须为 MySQL 进行语法修复;我不修复语法问题;那是你的工作):
< code>SELECT COUNT(*)+1 AS Id_iot -- 不是你,而是每个盲目使用它们的人
(SELECT title in_ner FROM bb_locations WHERE out_er.bb_locations_id = in_ner.id) AS 位置,
标题 AS 公告,
创建日期 AS 日期
来自bbs out_er
in_ner 和 out_er 是别名,即 FROM 子句中紧邻的表名的句柄;为了方便起见,我们在代码中的其他地方使用
我要建议的第一件事是,因为 MySQL 优化器很糟糕;它不理解上下文,inner 和 outer 可能被视为保留字。所以按照上面的代码更改它。
Subquery First, then the RANK() Function
Relax, son, we'll get there! Your speed is fine.
Preparation
The first thing, you really need to get access to a decent set of manuals, for your specific flavour of MySQL. I found ▶this one◀. As before, you have to do your own debugging, but I am now providing SQL that is as close to generic MySQL as possible. I've confirmed that everything we are going to be doing is entirely possible in that flavour of MySQL (I don't know what flavour/version yours is, except ENGINE=MyISAM).
Subquery
Ok, let's start again. I have written a ▶series of SELECTS◀, to lead your through the process. Please complete each one, and understand it completely before progressing to the next. If you have any questions, stop, and post the question.
The code is written and tested in Sybase; then downgraded for MySQL (from perusing the web, eg. the above site), and tested as much as possible in that state.
The first bit creates and loads three tables for use.
The first SELECT is a straight join of the three tables, no subquery. You need to get that to work; that is, understand what is does, fix any syntax problems; figure out the differences between the SQL I provide and the SQL runs on your server. And get used to making those changes. We can't keep stopping for that.
The second SELECT produces exactly the same result set. It introduces the concept of a Subquery, which is used to populate a single column.
Drive that bus. Respond when you're done or if your have problems.
Responses to Your Comments of 03 Dec 10 17:51
Straight Join I have never seen that way of doing joins before, I have always used left join, right join or inner join. Ok so for this first query we are just joining the two tables student and course with the studentcourse table sitting in the middle as the associative table. Results are repeated as expected because one student might be on more that one course and they will have a result for that course.
Yes.
That ( x=y in the WHERE clause ) is the traditional way of identifying joins, it is much more clear; the LEFT/RIGHT/INNER/OUTER JOIN syntax is the "new" way. Much more cumbersome AFAIC, but the learning is relevant because it is fundamental to what comes later. Feel free to convert to the latter syntax, and back again, for purposes of understanding.
Repeats ? That is not what repeats or duplicates mean. All the rows are discrete, true rows in CS. You should get the same 15 rows in every report (as we progress).
(ps when i direclty create the tables using queries you provided, the names are converted to all lowercase while the column names can still be camel case.)
MySQL is very strange. (It appears to be doing the naming conventions for us!)
. 2. Simple scalar query A few issues with query. You use the alias(in the scalar subquery) before you have defined what it is?(StudentCourse sc) I guess I always incorrectly assumed that you have to say define an alias before you use it.
You are thinking procedurally. SQL is a set-oriented language, for manipulating Relational sets of data.
The whole query gets evaluated and optimised in one pass. There is no "before" or "after". I am defining it in the same batch of SQL that I am using it.
I don't entirely understand the use of the alias 'in-ner' in the scalar subquery, is this to say that you want it to check each row individually(not sure how to explain this) instead of on a table wide check?Ie when you are doing this check make it local to the particular row you are on?(terrible explanation sorry).
For purposes of understanding/debugging, evaluate the subquery first (the contents of the brackets), alone. Understand it fully. Note the use of "sc" and keep it in your hat.
in_ner and sc are ALIASES, that is, handles for the table name that it sits next to in the FROM clause; that we use elsewhere in the code for convenience
in_ner is a descriptive name for the table referenced in the Inner Query, the Subquery
sc is a descriptive name for the table referenced in the Outer Query, which is only Outer because it has an Inner query, otherwise it would be a flat query
we could just as easy use fred and sally
Aliases such as in_ner and out_er are meaningful when the same table is referenced in both the Inner and Outer queries.
notice the join between the Inner query and the Outer query WHERE in_ner.CourseId = sc.CourseId
I have related the table referenced in the in_nerquery to the table sc referenced in Out_er query
Such a subquery is called a Correlated Subquery
See if you can visualise the Outer query (result set) as a grid, a spreadsheet, 15 rows by 4 columns.
Make sure you understand that Outer query, "easy" as it is. Notice that it is the same as (1. Straight Join), with a different method of populating one column.
As i understand it the scalar subquery asks for Name where the courseId's in Course and studentcourse are the same.(pretty straight forward) and is an alternative to saying that in the where,
Yes, exactly.
And notice that we are after only the Course.Name which is a 1::1 join from StudentCourse to Course, on CourseId. Notice exactly the WHERE clause in (1) that we are replacing in (2); in (1) it applies to all rows.
But because we are grabbing one datum; one cell; one item for a specific row/column; not all rows; not all columns, it is called a Scalar.
We are obtaining it using a subquery, which has to be constrained to the specific row. Therefore we need to relate the row from the outer query to the row in the Inner query.
so the Correlation between the Inner Subquery and Outer (specific row) is required.
And if we did not have that identification of the specific row, we would be loading rubbish into the Scalar, or it would return a Table (not a Scalar value) and the query would fail.
Try that, take the WHERE CourseId = sc.CourseId out
So that you know what the error message is, so that when it happens in future, you will know "Aha, I am returning a table, not a scalar; I am missing something in my Inner WHERE clause; I am not identifying a specific Correlated row". .
it is not quite "asks for Name where the courseId's in Course and studentcourse are the same"; it is getting the Course.Name for a specific StudentCourse.CourseId, which is identified from the outside, whatever sc row it is.
with the differnece that you can make this check row by row before the where.
you are thinking procedurally; there is no "row-by-row"; the dbms is set-oriented; the result set you are building is a set. Re-state the question is set terminology.
I used Course instead or in-ner, what is the point of using an alias in this case, is it just to show that aliases can be used?
Yes. And to highlight issues. And to differentiate the Inner Query from the Outer query. In the Inner query, the "inner" Alias, or any alias is not demanded. Only the Alias relating to the outer query is demanded.
Something I don't understand here is that when I try to do this, 'course.Name' it says unknown Course.Name in field list. this is the way that I have always defined that i mean Name in the Course table and not some other table. What would happen if I had two tables with a name column?
Exactly. If it were ambiguous, then you would have to supply the table name or alias; where it is not ambiguous, it is not demanded, but nice to have for documentary, clarity, purposes. You have to figure out why MySQL is not accepting it. Mixed case/lower case madness ?
I have also never seen that order by syntax, I can see that 1 and 4 mean the column numbers but why bother passing it two columns?
Huh ? Because I want the result set ordered by Course.Name in ascending order, and within that, by StudentCourse.Mark in Descending order.
If I did not state the order, MySql would produce the result set in whatever order it gets it from StudentCourse (chronological ?; by index ?). Whatever that default order is, find that out, you need to know it, and thus avoid an ORDER BY, when it is unnecessary.
Take the ORDER BY out and play with it.
Try ORDER BY 4 DESC, 1
It is not "passing", I am telling it what to do with my result set, in the one SQL command. The only passing you are doing is between your app (PHP ?) and MySQL.
2.1. Ok, when you finished with (2), and completely happy that you understand it, do this exercise.
SELECT (SELECT Name
FROM Course
WHERE CourseId = sc.CourseId
) AS CourseName,
() AS FirstName,
() AS LastName,
Mark
FROM StudentCourse sc
ORDER BY 1, 4 DESC
Produce the same grid format, we want the exact same result set as (1) and (2).
Fill in the two pairs of empty brackets with the appropriate subquery; ie. write a subquery to populate the FirstName column, and another to populate the LastName column
Responses to your Comments re Third Data Model
2.1. Perfect, yes, we move on. . You are cooking with gas, so if you don't mind, I will take your text, and annotate it a bit; notice the differences, they may or may not be subtle.
The correlated scalar subquery says that for each course id we need the highst mark, as opposed to the highest mark for all the courses. This is where the correlated aspect of this subquery comes into play because we are relating the outer query to the inner query for this particular row. [Yes!] The way that I am currently visualizing [That's it, use the visual part of your mind, not the serial part] it is that the outer query runs through the tables putting together the result table set, and each time it creates a row it runs the scalar subquery and picks out [a single value to fill the cell; here it is] the highest mark where the courseId's match, so when it is on a row where the course id is 66 then the scalar subquery is only looking for the max mark where the courseId is 66.
I could hardly have said it better myself.
There is no such thing as "result table".
Add one more definitive item.
The outer query defines the result set.
The subquery is independent of that; it is merely Correlated or Indexed.
Ok, so you have that SQL working, right ?
Now that you understand that, the next step is to visualise the result set, and to visulaise the subquery (3, unchanged) filling the entire column. if the above text was a balloon filling one cell at a time, then visualise hundreds of ballons, filling consecutive cells. Then visualise a bucket poured into the column.
Now leave that two dimensional result set alone for a minute, and visualise another layer on top of it. This is the parallel layer, where you write your subquery code.
If ever you have difficulty getting a subquery to work, go back to this, your way of visualising, one result set, and another layer for the subquery, which pours a bucket of scalars in, to fill the column. It eliminates all the well-known subquery coding bugs; removes the use of GROUP BY, DISTINCT, and all those ham-fisted methods of getting a long angry snake to fit into a jam jar. . Three more small steps before you proceed to (4).
2.2 Re-read my response (2) above, all the way down to this point. No skimming. This is because when you teach your mind something new and different, you need to re-inforce it. It is an officially recognised and labelled technique.
Responses to Comments of 08 Dec 10 20:49
2.3. Write that query (3) without using subqueries, and ensure you check the results. If you catch yourself laughing when you are writing the code, it is a good sign. As long as you produce the correct result set, you pass, but try to write the most efficient code (fewest COUNTS and GROUP BYs, etc). Do this only if you want to run circles around your peers, to be able to answer any "how do I code ..." question on your database.
I'm not sure what you mean by write that query without using sub-queries? I thought we wanted to avoid the use of group by's etc
Yes. Absolutely. You've walked forward. Now walk backward without tripping. This will really help your understanding of walking forwards, when it is better to use a subquery vs a join. Code the query with GROUP BYs and COUNTs. The fewest. Don't laugh.
2.4. Write the subquery (3) on your database, to produce a list of Bulletins, the outer query has to be FROM bbs only; with a count of likes, and a count of dislikes. So trunacte the tables and do 10 or 12 meaningful INSERTS, fibe minutes, big deal.
I used the method of using sub-queries on my database to put together a list of bulletins replies, count the number of reply likes and dislikes and get a particular users rating. it was great because I didn't have to use any group by's or counts and I didn't have to create temporary tables like I did for the bulletins.
Well, that's perfect. Now we are getting a bit of Relational Power in your spinach.
Now, go and look at this question and answer; ensure you compare the code. You've come a long way in just a few days.
When you finish (2.3), read your (2.4) query again, to refresh yourself, and move onto (4).
If you get stuck, replace the word "Rank" with "CountOfStudsWithHigherMark", and give it another go.
Responses to Comments of 11 Dec 10 13:14
2.3 I am having trouble writing that query without a scalar subquery. Scalar subqueries always made more logical sense to me even before I knew how to do them. That is why I said "I guess the problem I am running into here is, how do you refer to user-id = x in this particular row, not in all the row" in that previous question. Correlating the scalar subquery to the main query with and alias was the answer.
The (2.3) exercise is intended for you to:
really understand the incorrectness of the fat query with the GROUP BY (in a relational database using a set-processing relational engine) vs the correctness, elegance, and speed of the Correlated Subquery. You have achieved that. That will place you above your peers, in terms of SQL coding ability.
be able to identify when a fat WHERE clause and when a Correlated Subquery is appropriate. I am not sure, but it looks like you have achieved that.
be able to correct and debug this kind of issue when maintaining code written by others, and to be able to teach them the distinction. It sounds like you have a good visual, relational ability; which has been re-inforced by the exercise; and now you cannot go back to inferior methods. That is, you can understand and fix incorrect SQL code, but you cannot communicate that to others.
As long as you understand those distinctions and accept that, I am happy to drop (2.3) and move on.
Read your (2.4) query again, to refresh yourself, and move onto (4).
If you get stuck, replace the word "Rank" with "NumStudentsWithHigherMark", and give it another go.
Don't read further. The following is "old code"
Here's a ▶Quick Tutorial◀ on the RANK() operator (as it is commonly known). It is not ANSI SQL; it is an Oracle and MS extension. However it is not required if you understand Subqueries, which is why Sybase does not have it. I doubt MySQL has it, so you need to get your head around it. Understanding Scalar Subqueries is a pre-requisite. Sybase syntax, so whack your semi-colons in, etc. Feel free to ask specific questions.
I have never seen that approach of writing Rank = (SELECT.... Is that the same as (SELECT ...) as Rank?
Yes, () AS Rank instead of Rank = () are both legal SQL; MySQL may not like the latter form. The brackets containing the Subquery, of course. Note that Rank is the name of the derived column.
I have already stated that understanding subqueries is prerequisite. That means that millions before you have had this problem, and the lecturers figured out that you would suffer less frustration if you followed the lessons in the prescribed order. So forget RANK for now, and learn subqueries.
Try this (I supply ANSI Standard SQL; I do not have MySQL; you will have to syntax-fix it for MySQL; I don't fix syntax problems; that's your job):
SELECT COUNT(*)+1 AS Id_iot -- not you, everyone who uses them blindly
(SELECT title in_ner FROM bb_locations WHERE out_er.bb_locations_id = in_ner.id) AS Location,
title AS Bulletin,
created_date AS Date
FROM bbs out_er
in_ner and out_er are ALIASES, that is, handles for the table name that it sits next to in the FROM clause; that we use elsewhere in the code for convenience
in_ner is a descriptive name for the table referenced in the Inner Query, the Subquery
out_er is a descriptive name for the table referenced in the Outer Query, which is only Outer because it has an Inner query, otherwise it would be a flat query
we could just as easy use fred and sally
notice the join
I have related the table referenced in the in_nerquery to the table referenced in out_erquery
Such a subquery is called a Correlated Subquery
This is just an example, simple, so that you can learn Subqueries; purposely chosen to provide the same result set as one you are familiar with producing, using straight joins (bbs and bb_locations in the FROM clause, joining via the WHERE clause or JOIN syntax).
Because it produces a single value, it is called a Scalar Subquery (those that produce rows are Table Subqueries; and cannot be used like this, to load a single value into each row)
There is no suggestion that anyone should "use Subqueries instead of Joins". Absurd. Subqueries have their place, and Joins have theirs. Misue is a different thing.
Now, drive that bus. And don't talk to me about RANK until you can drive that bus around every corner in your database neighbourhood without killing any children.
I don't understand inner and outer, when I google them I get INNER JOIN what are they called so I can research further
Aliases. Refer above.
When I run that select statement I get this error You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE inner.Mark >= outer.Mark ) FROM studentmark outer ORDER B' at line 5
first, as per reasons detailed above, I can't write MySQL syntax, and debugging is your job
second, I realiise that you can't debug what you can't understand, so drop it for now (it has to do with RANK) and as you learn the MySQL flavour of SQL, all these things will be resolved
third, let me assure you that it runs on any Standard SQL server. It gets used in about 10 courses a year, so hundreds of participants per year. I just ran it again on Sybase, just to check.
first thing I would suggest is, since the MySQL optimiser sucks dead bears; it does not understand context, inner and outer are probably being treated as reserved words. So change that as per the above code.
好的,测试后您将把 SELECT 转换为 DELETE。左连接。对于单个删除是必需的,但其他情况下则不需要。这与 (2) 相同,只是将 WHERE 替换为 JOIN>
我已经推荐了(1),但你更有可能选择(3)。
Part II
Continuation of Part I, due to that Answer reaching maximum length.
Revised 14 Jan 11 - 05:40 PST
Comments re 11 Dec 10 13:14, Fifth Data Model and Responses
a. IDEF1X Design/Diagramming tool. I do not know of any freeware options. The MySQL design tool reportedly crashes often. If you are happy with my diagrams, I am happy to work with you for the duration, until the final model is resolved; ie. I provide the Data Model, and you can skip that task. For ongoing work, yes, you need a diagramming tool, perhaps not a database design tool. Refer my comments at the end of p2 in the Notation doc. . 29. Are you clear about the PKs and FKs in each table, as per the coloured tabs in the Fifth Data Model; can I remove the tabs now ? . 38. Closed. . 39. All the Dtm columns will be MySQL DATETIMEdatatypes. The variables you use for those columns should be the same. TimeStamp has a different meaning. Using the correct Datatypes is the first (big) step towards ensuring that the data is coorect and no illegal values are allowed to enter the db. Ie, only valid dates and times will be allowed. Further, you can interrogate any date or time component (eg. month or day name) from it. Check this document. . 40. No Problem. Instead of having just the one category hardcoded, how about (like the handling your of Permission), we implement an Category.IsRestricted and then Permission 5 becomes Post Restricted Bulletins. . 41. Done.
You should think about doing the same for Category and User. You want to be able to delete them by setting the Indicator, without removing the entry (and all the Bulletins, Responses, Replies, etc) fro the database. It has to be retained for historical purposes, but you need to disallow the User from logging in and doing anything. I have included this in the DM.
For such column names and Booleans in general, personally I prefer to identify the minority or exception case, as in IsObsolete.
20.2. Done. Table and column naming now progressed to InnoDB format. . Subquery responses in Subquery Answer.
Comments 13 Dec 10 13:14 EST and Responses
. 41. See (41) and next para, above. . 42. I meant: either Title or Description is enough; we do not need both. . 43.1. Implementation of Data Model. Go ahead. That's why I gave you the Physical yesterday. . 43.2. Design/Drawing. Go ahead. I have already commented in (a) above.
The Physical means a lot more detail required for implementation/coding: Datatypes; n::n Relations implemented as Associative tables; etc. You are pretty much ready to implement the Data Model, which means you need the Physical. And you already have the Associative tables figured out. Therefore I have taken the liberty of providing you with the Physical DM, even though you said you were in no hurry.
Note that Domains (User Defined Datatyptes) should always be used in a database, both for the DDL; the $variables you use. And a private Domain for each Primary Key. But this is not possible in MySQL, therefore the Datatypes are raw, regrettably.
Fixed length columns are much faster than variable length; I do not provide (advise) Var length. You are free to implement what you like.
Are you sure you need both Category.TitleandDescription ? I think not, but I have left it in until you confirm.
Enjoy the little blue glass buttons, and the navigation from the Collapsed Entities.
Please read the IDEF1X Notation document again, I expanded it last week.
Depending on how the Open Issues close, and any issues you may have, we can progress another edition, in the next day/night.
Comments 28 Dec 10 10:34 and Response
I have begun implementing the data model. I assume that the 6th data model is the physical model because it contains the associative tables.
Yes, I supplied that, and the Datatypes, because you said you were ready to implement.
There are still a few minor outstanding items. May be a good time to go through your question; all three of my answers, and check. Category.Title and Description, for instance.
I will put up a database dump once I am done.
That is not necessary, given that the model has the Dataypes defined; but if you do post it, sure, I will check it for you. Email may be better.
I will then put up a list of all the queries that I need to run on the database and begin writing them.
Very good idea, to take a structured and planned approach to the job.
Implementation of Physical model
(39) With mysql I am not able to assign more than one primary key so I am just going to make them unique and not null as you suggest in the documentation. Do you think it would be a good idea to index them as well?
Not sure what you mean, what is "them" ?:
you can never have more than one Primary Key on a table; the Alternate Keys are Unique, one of them is "primary"; that is carried as FK in the child tables.
with InnoDB (what you said you will get), you can define PRIMARY KEY constraints (which is equivalent to UNIQUE, NOT NULL)
with MyISAM (what you have now), you need an Index, UNIQUE, NOT NULL for the Primary Key (above the line in the model)
for either InnoDB or MyISAM, each Alternate Key (AKx[.y] in the model) must be defined as an additional index, UNIQUE, NOT NULL.
Comments 07 Jan 11 14:08 and Response
(40) Could you explain why the category.CategoryCode is a char of 4 characters. Why not just use an number like we do for user?
40.1. The idea is to use good natural Identifiers. Numbers are meaningless to users. If we didn't have a large no of Users, and User churn, I would not have used a number there either. A CHAR(2) or (3) or (4) allows them to pick meaningful short code for the long Category.Description, and it is small enough to be carried as a Foreign Key in user_category and bulletin.
For the developer, when testing and debugging, that short code in a list of say bulletins, will be very handy.
(40) I don't quite understand permission for category and location. Lets say that I want all users to be able to post to the Travel category. Would I set the permission of this category at 4? Why do we need to bool IsRestricted when we are giving a permission to the category and location?
40.2. I have not changed the concept or essence of Permission; it was your idea, and it remains exactly as you explained it to me.
(All I did was implement permission as a table.)
40.3. Refer (13) and (40) original exchange. category.IsRestricted defines restricted categories; there are two categories of categories, Restricted and Unrestricted. The users need a permission of 5 to post bulletins re Restricted categories, and 4 to post bulletins re Unrestricted categories.
40.4. But whoa, son, are you introducing a change or extension; eg. match the permission of the user to the permission of the category, thus allowing far more than two categories of categories ? Please don't. That would mean permission means one thing re category and a different thing re the rest of the system. Or if you do, then we have to resolve the exact need first, then implement it as a change.
40.5. Location (now office) is exactly the same for that bit (40.2) and (40.3). If you are referring to the text at the bottom, it is my small mistake, will correct it.
40.6. As per (14), office additionally has a single AdministratorId (UserId). Shown on the model as (permitted) user.
40.7. But that brings up an issue: who can administer categories ? Right now anyone with permission 5 or greater, which is a different thing. I think we need something explicit, a permission 6 = Administer Category.
Other
Processed your DDL and returned.
Data Model Updated. Number of small clarifications and two minor errors corrected.
Comments 08 Jan 11 14:08 and Response
(I think that was 09 Jan not 08 Jan ... I did check for updates.)
(40) I don't quite understand permission for category and location. I haven't changed anything. Disregard the content of the previous question as explain what permission would be set to allow users to post to the 'Travel' Category. In my implementation I simply had a permission column. If a given user had the required permission or greater then they could post to a category, is that how the new system works?
Yes. Unchanged. category.permission has nothing to do with it. They need user.permission 4 for unrestricted categories.
If the category.IsRestricted, they need user.permission 5.
Quite separately, an user needs user.Permission of whatever category.Permission is, in order to administercategory. Do not use values less than 4.
(41) Query Delete a bulletin and all its associated replies and ratings.
I did not expect that, are you sure they have no need to keep all past bulletins for historical or audit purposes ?
Anyway, let's deal with that on the basis that deleting bulletins is allowed ...
I don't even know where to get started on this. In the past I have joined would have joined up the bulletin table with the response table and the response table with the response rating table and the bulletin-rating table where the bulletin id = x and deleted them. But now any one particular bulletin is identified by three columns: the OfficeCode,IssuerId and BulletinDtm. Which are carried to the child tables as foreign keys. For a start, how do I store indicate which bulletin is to be deleted in my php? Usually I would have a link like this index.php?action=delete&bulletin-id=5. Now will I have to have a link that is index.php?action=delete&OfficeCode=20001&IssuerId=34&BulletinDtm=14:02... I really have no clue how to do this?
a. I can't help you there, I am a database and SQL expert, not a php or MyISAM expert. You will need to post that as a new question on SO or the MySQL boards.
b. As far as my reading of that subject has taken me, I don't know the syntax, but yes, it can be done, it is normal. I checked before recommending composite keys to you. The corrected DDL succeeded, and the indices are confirmed, correct ?
c. The issue is simply the syntax required for composite or compound keys; and working with index.php. Something like:
index.php? action=delete & OfficeCode=x and IssuerId=y and BulletinDtm=z
d. Why can't you use mysql_query instead of index.php and thus use full SQL ? As I understand it, it works with MyISAM. Then you can use:
`$sql = "DELETE $table WHERE OfficeCode=$OfficeCode AND IssuerId=$IssuerId AND BulletinDtm=$BulletinDtm";`
e. Delete response_rating first; then response; then bulletin_rating; then bulletin. When they switch to InnoDB they will have less changes to make.
f. Most important, you will have to get them to identify the basis on which a bulletin can be deleted. ANy and all bulletins should not be deleted. Something like "no activity for one year" or "closed" (which means an added column), etc.
Comments 10 Jan 11 14:08 EST and Response
(41.10-Jan-11) No problem, the method is fine, and I have detailed related issues which need address under (41.*) above. (41.f) still needs an answer ... other than permissions, is there any basis for deleting bulletins ?
Comments 10 Jan 11 13:48 pst and Response
SO Editing. Don't worry, it is not you. The site is of poor technical quality. The editing is hopeless (and believe me, I have tried to work with it and around it, to make my Answers appear even somewhat like I want them to appear). It cannot handle indents or more than one level of numbering correctly.
Delete Basis. Ok, you have a valid basis. And the users who wrote responses would not mind if they were deleted without being asked ?
(41) What you are looking for is a "cascading delete" in Standard SQL, which is defined in the Foreign Key clause (which you do not have in MyISAM). Each INSERT/UPDATE/DELETE verb applies to one table only, and may affect other tables by REFERENCE.
For non-standard SQLs, you have the DELETE multiple_table method (non-standard syntax).
First, it is very important to understand this, before anything else. The FROM and WHERE (or JOIN) clauses in a DELETE command are separate to the DELETE itself; they are in fact a SELECT. The idea is: DELETE table_one (SELECT FROM table_one, table_two WHERE join_conditions).
Therefore:
name the four tables in the DELETE (target)
name the four tables in the FROM (how you find them, via SELECT)
ensure you have the correct (complete) JOIN clauses for the four tables; which you can test via a SELECT
which means, JOIN ON OfficeCode, IssuerId, BulletinDtm (the bulletin PK affecting the thre child tables)
use NATURAL or INNER joins, not left joins (be explicit, do not mix them up, as a general rule)
ensure the WHERE identifies the specific bulletin composite Primary Key to the deleted.
a. Don't be lazy. Write four delete statements, bottom up. That's what we have to do in the big end of town, where we do not have "cascading deletes". Write the delete for rating_response; then copy-and-paste, and delete one line of code each time. I do not understand the angst or avoidance.
b. I repeat, do not use left, right, or any kind of outer join (which is only required for the single all-encompassing delete). Use straight inner joins only (which is not a problem with 4 delete statements). Any and all upset you are experiencing is due to your need to use one delete. Give that up, and the upset and complication disappear.
I just wrote this code for another question. That is a single SELECT command. Three-column PK times four Subqueries. I do not understand the need to avoid long (demanded, again, due to SQL being cumbersome) commands. And I didn't even use the JOIN syntax. Took me all of ten mins to write, plus five mins to test. What exactly, is the big deal ?
c. You have not forgotten the power of Relational keys, that you recognised some weeks ago, have you. Eg. ability to grab bulletin from rating_response, without having to join with rating. If you succumb to your single-column-key desires, you will lose all that. SQL is cumbersome. But that is all we have. Deal with it. The non-SQLs try to "make life easy" but in fact, introduce all sorts of unnecessary and avoidable complications. Case in point.
Comments of 13 Jan 2011 21:18 PST and Response
Deletes. Three flavours. Great. Hopefully you will have the data values in $variables, so there will not be that form of repetition. For testing, that is fine.
Delete x Four Tables. (not "Indiviudally deleting records", which is a different thing altogether; each delete except the last could net hundreds of rows). I trust the cut-and-paste took seconds. You need to be careful about forgetting to change the table names.
Single Delete Command. $variables for the first triplet. You could use column names in all but the first triplet.
Ok, so you will convert the SELECT to a DELETE, after testing. Left Joins. Required for the Single Delete but not otherwise. That's identical to (2) with the WHERE replaced with JOIN>
I've already recommended (1), but you are more likely to go with (3).
Dont be afraid of joins. If i were you i would cut down on all the DB logic you need to write and use an ORM like Doctrine or Propel, it will make things infinitely easier to design and maintain - including all those joins youre trying to avoid.
发布评论
评论(5)
第一部分
于美国东部时间 10 月 10 日 12 月 9 日 01:00 修订
已查看您的 DDL。好的。我们需要退后一步,首先组织您的数据库。这将解决您一半的问题(您的 SQL 将直接、快速、索引更少、不需要临时表)。有一段时间我想,啊哈,你有你的专栏,它一定很稳定,但没有机会。从头到尾,好吧。看一下这个实体关系图(在我们得到正确的 ER 之前,在数据模型上工作是没有用的,即实体、关系和属性),并检查它是否正确。
方法是回答以下问题(简短的回答即可)。这些问题澄清了实体和业务规则。如何理解一般数据库,特别是您的数据至关重要。您已经走了很长的路,靠自己的力量,所以我们可以从那里开始。
我认为▶这篇文章◀ 可能对您有所帮助,以了解应遵循的正式阶段;我们在这里短路了。
最重要的是,完全彻底地忘记功能和任何编码要求。数据必须独立于应用程序进行建模,就像数据一样。函数建模是一门不同的科学。首先做对;然后得到另一个权利;两人一起演奏优美的曲子。尝试将它们塞在一起;
为了简洁起见,也为了任何人阅读本文,我使用了封闭和开放部分;当一个开放项目(讨论)被关闭时,我会使其简洁,并将其移至“已关闭”部分。保持编号,因为有时事情会再次困扰我们。您可能希望这样做,甚至删除您这边的讨论。
漂亮图片的链接在最后。
抱歉:编辑不起作用;子编号不一致
已关闭问题
...WHERE IN ()
更复杂的代码来实现您声明的目的。.
.
已确认:没有用户,公告(bbs)就不存在;用户发布公告,整个周期就开始了;然后邀请回复和评分。
3.1 已确认:实际上只有一个公告板,并且它在数据库中不作为事物存在。
3.2 已确认:该组织永远不会有多个公告板,并且分类和类别都由类别表/函数充分处理
已删除。
已确认:公告和回复之间的区别在于,回复依赖于公告而存在,它们没有标题,并且不按位置或类别进行分类,因为它们依赖于公告本身而存在。
已删除。
已注意到评论。已解决。
7.1.对于另一用户提交的每个公告,每个用户可以发布多个回复。
7.2.对于用户提交的每个公告,该用户可以发布一个或多个回复。
7.3.已删除。
7.4.已删除。
.
8.已确认:每个用户最多可以在公告上发布一个评分(可以撤销/更改)
.
9. 已确认:每个用户最多可以对一条回复发表一个评分(同上)
10.1.给定:用户名来自组织,是标识员工的唯一名称。例如,电子邮件是 [email protected] - 身份验证是使用 ldap 完成的需要连接并检索有关员工的其他信息
10.2。已确认:名字、姓氏...出生地等保留为(传统)列,以确保
人物
不重复。.
11. 鉴于:目前,我们可以通过组织内普遍熟知的临时名称来识别我们的办事处,因为我们只有大约 3 个主要办事处和许多外地办事处。例如华盛顿特区或弗吉尼亚州办事处。总的来说,我认为我们会尝试将总数保持在 20 以下。我还想记录每个位置的确切地址,因为这可以用于唯一地标识用户的办公室。
StateCode+Town
作为PK;IsMainOffice
作为布尔值。.
12. 已确认:
Category
的Description
和Name
为必填项。.
13. 鉴于:用户将无法向某些类别发帖。只有具有足够高权限的用户才有权向某些类别发帖。
User, Location, Category
中的Permission
是评估此类权限的方法。.
14. 已确认:
Location.Administrator
是Location
管理员的UserId
。.
15. 鉴于:永远只需要喜欢或不喜欢。我认为不需要保持中立立场,因为这与不投票是一样的?点赞似乎与诚实发布的公告回复更相关。也就是说,“我看到了你的回应,我不会写自己的回应,而是同意你的观点——现有的公告板在某种程度上是组织中的社会方面,我认为喜欢和不喜欢/同意和不同意会产生一定程度的争议,从而鼓励参与。然而,喜欢或不喜欢公告可能并不总是完全合适的。
15.1 提供:
Like
作为BulletinRating
和ResponseRating
中的布尔值。这将需要对每次访问进行解释。15.2.当它不再是布尔值时,可以将其更改为RatingCode,并实现为查找表。然后,名称由连接确定,并消除解释。我在第一个数据模型中画了这个,这样你就可以明白我的意思
15.3。在第二个数据模型中删除。
.
16. 已确认:每个用户都有一个家庭
位置
(除了他们感兴趣的位置
列表)。.
17. 已确认:按照 (13) 的
许可
。.
18. 已确认:根据数据模型,可能需要进一步的权限。
18.1.如果您现在这样做,您就不必担心组织何时决定阻止某个
人
发布响应
或公告
,或者对他们进行评级;并希望昨天实现该功能。18.2.即使您没有实施它,也要在您实施的价值观之间留下差距。
.
19 已确认:
公告
是关于位置
。19.1。已确认:没有
位置
的公告
19.2。已确认:没有
公告
没有位置
。19.3 已确认:没有
用户
(声明性)就没有公告
。但到目前为止,我们还没有办法限制该User
;因此,任何用户
都可以为任何位置
插入公告
(您可以将其限制在代码中,例如位置
每个用户都感兴趣
已确认:没有
公告
且评级为用户
,则不存在BulletinRatings
。19.4 已确认:没有
公告
就没有响应
19.4 已确认:没有
响应
和响应
就没有响应评级
。评级用户
。但是,可以有独立的
用户
、位置和
类别。20. 如果您不介意,我将提供命名约定等。它们应该是不言自明的,并且只有当您开始编写 SQL 代码时,该值才会显示。如有不妥请追问。首先,所有名称都是单数。混合大小写更容易阅读(SQL 语言应该使用大写)。
20.1.我的经验是 table_name 相对于 tableName 来说是真正的技术形式,用户不喜欢它们;一致的混合大小写是每个人都喜欢的。这是无法改变的事情之一,因此请谨慎选择。
.
21. 对于您需要将表格分组在一起的需求(这很好),请记住这是一个物理问题。在逻辑数据模型级别,表具有正常的名称,不受物理问题的影响。想象一下,物理表的前缀类似于(请使用大写):
-
REF_
用于参考(例如用户)和查找表-
BUL_
用于公告系统.
我无法用大写字母命名表格?我不知道为什么。我不知道为什么我不能使用大写表名。与使用 MyIsam 数据库表有关吗?
.
22.
rank
(全部)可以直接从数据库中导出(记住,在数据建模期间不要担心代码)。如果存储的话,就是Normalization error;重复的列;必须保持最新;这可能与派生值不同步;这称为更新异常。第五范式消除了更新异常。这是我的最低标准化水平,所以这就是你将从我这里得到的。22.1。我根本不干涉排序顺序或受欢迎程度问题;事实上,听上去你还没有关闭该功能。我只取出冗余数据,即排名列,作为标准化过程的一部分。
22.2.这是▶快速教程◀ 关于 RANK() 运算符(众所周知)。它不是 ANSI SQL;它是 Oracle 和 MS 的扩展。然而,如果您了解子查询,则不需要它,这就是 Sybase 没有它的原因。我怀疑 MySQL 有它,所以你需要了解一下它。了解标量子查询是先决条件。 Sybase 语法,因此请输入分号等。请随意提出具体问题。
.
我从未见过这种写 Rank = (SELECT...) 的方法与 Rank 的 (SELECT ...) 相同吗?
.
22.3。需要了解为什么,完全没有问题。只有小孩子才会盲目地遵循简单的规则,而你肯定不是其中之一。
.
23、确认:
users.total_bulletins
是多余的;可以推导出来。已删除。.
24.你所有的PK都是Id。您还没有厌倦在代码中迷失吗?忘记在所有移动的东西上粘贴
Id
物联网PK,让我们看看您的用户如何识别他们的实体;哪些实体是真正独立的,哪些实体依赖于独立实体。24.1。切勿使用
Id
或任何此类形式。如果是 PK,请使用完整形式。24.2.调用location_id,location_id,无论在哪里,包括PK表。例外是当你需要展示角色时。这将在数据模型中变得清晰。
.
25. 您没有声明性引用完整性,没有定义外键。出于多种不同的原因,这是个坏消息。一旦这些问题得到澄清,请添加它们。DRI 意味着尽可能多(如果不是全部)在 SQL 中声明完整性。 ISO/IEC/ANSI SQL 标准允许这样做,但市场的免费软件并未提供该标准,并且正在慢慢迎头赶上。这意味着服务器不允许在FK表中添加行,除非父表中存在PK。 MySQL 最近为外键提供了 DRI。对于FK,请参考▶这个文章◀。
25.1。对于 CHECK 约束和规则,您必须在代码中实现它们。
我的外键就像,users-id(fk) = users.id(pk) 除了我所做的之外,我不知道如何添加它们,但一旦我知道如何添加,我肯定会这样做。
二十五。 注释已注明。我不是 MySQL 专家。是的,这些都是你必须自己解决的问题。总的来说,从我的观察来看,MySQL 是没有腿的;对于任何 SQL 风格的东西,你都需要 InnoDB。
.
27. 鉴于:我重新考虑了公告的排序要求。用户可以按时间顺序排序——简单、有意义。用户可以按照最新回复公告的日期对公告进行排序。然后我们就可以忘记排名,并且应该很容易按照公告的最后回复时间按时间顺序对公告进行排序?你有什么想法。
开放问题
(无)
数据模型
好的,假设您的 ERD 没有问题,并实现了所有已解决的问题,我已经对数据进行了建模,并准备了第五个数据模型(2010 年 12 月 9 日)供您审核。我确实需要关于此的更多反馈、问题等。我很难接受这件事已经完成了。也许最好开始为您的问题领域编写真正的代码。
链接
▶IDEF1X 符号链接◀ 在阅读数据模型之前,您确实需要阅读并理解这一点。
▶第五公告数据模型的链接◀实体关系图位于第一页,后面是数据模型。
按键几乎都是直接的IDEF1X(除了我作为对位提供的UserId);这意味着钱包关系密钥。未增强且未针对物理考虑进行优化。在你犹豫之前,首先注意它们、记录它们并评估它们。当然,我们可以添加
Id
物联网密钥,但在此之前,让我们确保我们了解我们将丢失什么。注意符号文档中的标识符(实线)。脊柱,系统的椎骨是
位置...公告...响应
。请注意,键实际上实现了许多业务规则。
注意我渲染的自然层次结构。看看它对你是否有任何意义。
动词短语非常重要;看看它们是否意味着什么。
关于第一个数据模型和响应的评论
我的一个问题是该位置的主键将用于形成子主键?(它们由实线连接)我不太明白这个概念< /em>
或者为什么用户和公告之间不存在这种关系?
根据上面进一步陈述的意图,由于我现在已将评级显示为表格以及渲染结果,一旦我将删除它
我认为权限应该是一个实体。
公告
PK 现在为(StateCode、Town、UserId、SequenceNo)
。需要明确的是,SequenceNo
位于StateCode、Town、UserId
内:对于 Sally 的有关 MO/Billngs FO 的第 5 个公告,它将为 5。注意,用户设置
BulletinsPerPage
等与User
是1::1,所以它们在User
中;子表将不正确。已更正印刷错误。
关于第二个数据模型和响应的评论
Bulletin
和Response
的 PK 已更改以反映 (7)。BulletinNo
和ResponseNo
已替换为BulletinDate
和ResponseDate
(以前是CreatedDate),以便允许每个
用户
每个公告
进行多次回复。关于第三个数据模型的评论和回复
相信您度过了愉快的时光。
至少在 30 年前(据我所知),行业巨头们曾进行过这样的争论。名字总是单数的。表是名词。动词短语是动词。这不仅限于数据库命名约定,它适用于文档、论文、学位论文等。您可能在文档末尾有 5 个结论,但在目录和页面顶部都有章节或章节标题是“结论”。
在大学一路与他们抗争之后,当我开始第一份有偿编程工作,并看到规则在现实世界中的重要性,而不是我们在大学里的理论争论时,我就放弃了它浪费时间。我浪费的所有时间和精力都被释放出来去做富有成效的工作。从那时起,我不再质疑巨人;我只是接受。他们的思想比我的思想更伟大。这就像接受标准,或者在法律或上帝范围内行事。我没有真正非常充分的理由去做任何非法的事情。
无论如何,这些规则所支持的语言(讨论、SQL、文档)的易用性无法得到充分的解释;当你编写越来越多的 SQL 代码时,它就会变得清晰。
您始终可以自由地使用您想要的任何内容。我只提供单数。
对我来说很好。
但是您需要记住,所识别的序列(例如非 PK 唯一索引或备用键)中的这两个元素是建立人员唯一性所普遍需要的。删除它们会导致两件事。首先,您将无法再识别
用户
之间的唯一性(因此您可能有重复的行)。其次,AK 变得不唯一,成为反转条目。要点是(与其中一篇文章相反),任何与
User
PK 为 1::1 的列都应位于User
中。所有首选项设置。由于我们清理了InterestedLocations
和InterestedCategories
,我只知道剩下的BulletinsPerPage
;但是我敢肯定还有其他。iSpReference2
是EG。 boolean;numPreference3
是一个EG。整数。等等。你可以告诉我真正的偏好是什么。(让我们在复数中尝试一下:...
用户
pk的任何1 :: 1的列,应驻留在用户
中。只是不做对我来说,我挂在破碎的英语上,我对母语有些珍贵。)数据模型已更新。
很棒。让我知道您何时对此感到满意,我会给您物理模型。
动词?
评论re 06 12月10日20:38 EST(小更新)
。
28。当然,PK仅出现PK,而FK列名称与PK列名称相同。但是,当FK中有多个OCC(看看
响应
)时,有三个userIds
),我们需要区分它们。在IDEF1X术语中,这称为角色。发出Bulletin
的用户
的角色是发行者
,依此类推。显然,最好使用该名称,并在整个层次结构中保持一致(userId
inBulletin
,然后当我们到达Response> Response> Response
时,有两个,并且需要差异化,请将其更改为 。 >因此,绝对清楚的是用户ID
作为FK,并且该角色是发行者
; 同样,我们有许多DateTime列(如果您愿意的话,请日期;否则DTM)。
.
29. IDEF1X符号文档是否没有意义?
对于
Bulletin
:(statecode,town)
发行的位置用户ID
删除此
Bulletin
的所有wenderseratings
,在这四个<代码>公告列。.
30。因为
(状态,城镇)
是位置
的PK,随身携带。它构成了Bulletin
pk的一部分,因此任何因表都带有这些列,因为它们携带Bulletin
pk。我们以前曾确定
(州,城镇)
是PK,我将按照将其保留为请参阅(38)以进行更改。.
33。值得讨论。是的,如果要在(例如)显示
响应
时显示它,并且用户了解用户名
。不,如果是30个字节,并且还有一个唯一的4个字节用户ID
。当您最终确定约6列30字节键太麻烦了,无法迁移到孩子时,想法是有意识地意识到自己放弃的选择。userId
作为典型的id
pk,因为它被携带/迁移到几个子表中。.
34。没问题。
类别
已经拥有它。我将将订单
更改为listorder
。.
35。当然。根据我阅读和听到的内容,我对此感到非常满意。但是,在您编写代码之前,我希望更多来回获得一些信心。或者,将其视为一种学习体验,并接受模型和代码以后可能会发生变化。您要我现在生产身体吗?如果您给我所有更正,我将发布下一个版本。我希望
用户
中的首选项。另外,迅速浏览功能并检查您是否拥有所需的所有列。为了学习和兴趣,请查看其他一些答案。
.
36.加入。您只需加入
四个三列而不是一列。 SQL与Joins很麻烦,而应该使其更容易的新语法实际上更加麻烦。我的编码器从不写入加入:我们节省了时间和错别字。我有一个给定两个或多个表的PROC,将与所有列和连接在一起生成代码。我不了解MySQL,无法为您转换。数据模型更新了。
。
评论RE RE 08 DEC 10 20:49,第四个数据模型和响应
。
检查上面的上一节,有小更新。
IDEF1X:您的速度很好。
请注意,孩子总是“继承”父pk,作为FK(固体或断线),否则它们之间没有关系。无论如何,通过使用这些孩子中存在的这些列来形成孩子PK,我们携带含义(这就是固体和破碎之间的区别)。因此,我们不需要为孩子寻找独立的标识符。当您进行编码时,此方法中的关系能力将在稍后变得清晰。
我们正在处理的部分是标识符:自然与非自然;有意义的与毫无意义。稍后,您将看到如何在由父pk形成子PK时使用引擎的关系能力。 (您的姓氏与父亲的姓氏不一样吗?)
了解关系数据库及其能力也很重要。当我们从OO的角度接近数据库(例如),并将其视为使我们的课程“持久”时,那将丢失。因此,我们将尝试学习和使用关系术语。当您去法国并期望他们会说美国人并使用相同的货币时,这会变得困难。学会说10个法语单词,他们张开双臂欢迎您,您与当地人会有很大的不同经历。
无论如何,继续实施模型。只是意识到我们可能会在某个时候进行更改。保存所有DDL。将所有测试数据保存为插入语句或表备份或字符格式导出(不知道MySQL在此领域无法做什么)。
.
37.1。处理,n :: n与
Office
&amp;类别
。当我们进入物理模型时,您只会“看到”。37.2。完毕。
37.3完成。
.
38。太好了。也短。请注意,他们将永远无法在同一邮政编码中拥有两个
办公室
。数字(5,0)很好,但我认为美国正朝着7位数字迈进。没关系,您可以弄清楚。它是Office
的绝佳PK。现在,本列是地址
的一部分,可能是zipcode
,已提高到更高的目的,而无需重复;由于我们将其携带在5个子表中,因此我们希望按照先前的惯例清楚pk名称,因此我们将其称为officeecode
;OfficezipCode
可能很愚蠢。我们需要在
name
上进行唯一的索引,以确保它们不会添加两个具有相同名称的Office
。注意,出于解释目的,这实际上是Office
,替换(statecode,town)
的逻辑键,并且仍然如此。我仍然认为您可能需要
statecode
和town
作为快速引用(除了坐在address> address
中的某个地方,数据模型已更新,现在可用于第五个审查。您没有说明您的喜好,
... date
vs... dtm
。我正在使用后者,因为它更加熟悉,也可以识别时间组成部分。易于改变。这个答案已达到最大长度。继续在“第二部分”
Part I
Revised 09 Dec 10 01:00 EST
Looked at your DDL. Ok. We need to take a step back and organise your database first. That will solve half your problems (your SQL will be straight-forward; and fast; less indices; no temp tables required). For a while I thought, aha, you have your columns, it must be stable, but there is no chance. Top down from scratch, ok. Have a look at this Entity Relation Diagram (no use working on the Data Model, which is Entities, Relations and Attributes, until we get the ERs right), and check that it is correct.
The way to do that is, answer the following questions (short answers are fine). These questions are clarifying the Entities and Business Rules. How you understand databases in general, and your data in particular is crucial. You have come a long way, on your own, so we can take it from there.
I think ▶this post◀ might be helpful to you, in order to understand the formal stages that should be followed; which we are short-circuiting here.
Most important, totally, and completely, forget about the function and any coding requirements. Data has to be modelled independent of the application, simply as Data. Function Modelling is a different science. First get one right; then get the other right; and the two together play beautiful tunes. Try jamming them together; doing both tasks at the same time, and they won't even make a suburban garage band.
For brevity, and the sake of anyone reading this, I with use a Closed and Open Section; when an Open item (discussion) is closed, I will make it concise, and move it to the Closed section. Maintain the numbering, because things sometimes come back to haunt us. You may wish to do the same, or even delete the discussion on your side.
The links for the pretty pictures are at the end.
Apologies: the editing does not work; sub-numbering is inconsistent
Closed Issues
...WHERE IN ()
for your declared purpose..
.
Confirmed: a bulletin (bbs) does not exist without an user; an user issues a bulletin, and that starts the whole cycle; then invites replies and ratings.
3.1 Confirmed: There is really only one bulletin board and it does not exist as a Thing in the database.
3.2 Confirmed: that the org will never have more than one bulletin board, and the classifications and categorisations are all adequately handled by the Category table/function
Deleted.
Confirmed: The difference between bulletins and replies is that replies are dependent on a bulletin to exist, they do not have a title and they are not categorised by location or category because they are dependent on the bulletin itself to exist.
Deleted.
Comments noted. Resolved.
7.1. For each single bulletin submitted by another user, each user can post more than one reply.
7.2. For each single bulletin submitted by an user, that user can post one, or more than one reply.
7.3. Deleted.
7.4. Deleted.
.
8. Confirmed: each user can post at most one rating to a bulletin (which can be revoked/changed)
.
9. Confirmed: each user can post at most one rating to a reply (ditto)
10.1. Given: username comes from the organisation and is the unique name that identifies employees. For example emails are [email protected] - authentication is done with ldap and this is required in order to connect an retrieve other information about the employees
10.2. Confirmed: FirstName, LastName ... BirthPlace, etc remain as (the traditional) columns for ensuring
People
are not duplicated..
11. Given: At the moment we can Identify our offices by casual names which are generally know within the organisation, since we only have about 3 main offices and many field offices. So examples would be Washington DC or virginia field office. In total I think we will try and keep the total below 20. I want to record the exact address of each location as well because that could be used to uniquely identify offices to users.
StateCode+Town
as PK;IsMainOffice
as boolean..
12. Confirmed:
Description
andName
forCategory
are required..
13. Given:Users will not be able to post to some categories. Only users with sufficiently high rights will have the right to post to certain categories.
Permission
inUser, Location, Category
is a method of evaluating such rights..
14. Confirmed:
Location.Administrator
isUserId
of admin for theLocation
..
15. Given: There will only ever be a need for a like or a dislike. I don't think there needs to be a neutral position because this is the same as just not voting? Liking seems more relevant to bulletin replies that posts to be honest. Ie 'i see your response and instead of writing my own I will just agree with you - the existing bulletin board is somewhat of a social aspect in the orgainsation and I think liking and disliking/agreeing and dissagreeing creates a level of controversy that encourages participation. However liking or disliking a bulletin may not always be entirely appropriate.
15.1 Provided:
Like
as boolean inBulletinRating
andResponseRating
. This will require interpretation on every access.15.2. When it is no longer a boolean, it can be changed to a
RatingCode
, and implemented as a Lookup table. The names are then determined by Joins, and interpretation is eliminated. I drew this in the First Data Model, so that you could see what I meant15.3. Removed in the Second Data Model.
.
16. Confirmed: each user has a home
Location
(other than the list ofLocations
that they are interested in)..
17. Confirmed:
Permission
as per (13)..
18. Confirmed: Further Permissions may be be required, as per Data Model.
18.1. If you do this now, you won't have to worry about when organisation decides to prevent a certain
Person
from postingResponses
orBulletins
, or Rating them; and wants that feature implemented yesterday.18.2. Even if you do not implement it, leave gaps between the values you do implement.
.
19 Confirmed: a
Bulletin
is about aLocation
.19.1. Confirmed: There are no
Bulletins
without aLocation
19.2. Confirmed: There are no
Bulletins
without aLocation
.19.3 Confirmed: There are no
Bulletins
without aUser
(declarative). But so far we have no way of constraining thatUser
; therefore anyUser
can inset aBulletin
for anyLocation
( you could constrain it in code, eg. toLocations
eachUser Is Interested In
.19.4 Confirmed: There are no
BulletinRatings
without aBulletin
and a ratingUser
.19.5 Confirmed: There are no
Responses
without aBulletin
.19.4 Confirmed: There are no
ResponseRatings
without aResponse
and a ratingUser
.19.7. But, there can be
Users
, Locations, and
Categories`, independently..
20. If you do not mind, I will provide naming conventions, etc. They should be self explanatory, and the value will show up only when you start coding SQL. Please ask, if anything isn't. For starters, all names are singular. Mixed Case is easier to read (you are supposed to use capitals for SQL language).
20.1. My experience is table_name as opposed to tableName are really technie forms, and users do not like them; Consistent mixed case is liked by everyone. It is one of those things that is impossible to change, so choose carefully.
.
21. For your need to group tables together, which is good, keep in mind that that is a Physical issue. At the Logical Data Model level, the tables have normal names, uncluttereded by physical issues. Imagine that the physical tables are prefixed with something like (and please use capitals for this):
-
REF_
for reference (such as User) and lookup tables-
BUL_
for Bulletin system.
I am not able to name tables with uppercase letters? Im not sure why. I don't know why I can't have uppercase table names. Is it to do with using MyIsam database tables?
.
22.
rank
(all) can be derived directly from the database (remember, do not worry about the code during Data Modelling). If you store it, it is a Normalisation error; a duplicated column; which has to be kept up-to-date; which can get out of synch with the derived value; which is called an Update Anomaly. Fifth Normal Form eliminates Update Anomalies. That is my minimum level of Normalisation, so that is what you will get from me.22.1. I am not interfering with the sort order or popularity issue at all; in fact, by the sounds of it, you haven't closed that functionality. I am only taking redundant data, the rank column, out, as part of the Normalisation process.
22.2. Here's a ▶Quick Tutorial◀ on the RANK() operator (as it is commonly known). It is not ANSI SQL; it is an Oracle and MS extension. However it is not required if you understand Subqueries, which is why Sybase does not have it. I doubt MySQL has it, so you need to get your head around it. Understanding Scalar Subqueries is a pre-requisite. Sybase syntax, so whack your semi-colons in, etc. Feel free to ask specific questions.
.
I have never seen that approach of writing Rank = (SELECT.... Is that the same as (SELECT ...) as Rank?
.
22.3. Needing to understand why, is no problem at all. Only children blindly follow simple rules, and you are certainly not one of them.
.
23. Confirmed:
users.total_bulletins
is redundant; it can be derived. Removed..
24. All your PKs are Ids. Haven't you gotten tired of getting lost in the code yet ? Forget about sticking
Id
iot PKs on everything that moves, let's find out How your users Identify their Entities; what Entities are truly Independent, and the other which depend on Independent Entities.24.1. Never use
Id
or any such form. Where it is a PK, use the full form.24.2. Call location_id, location_id, wherever it is, including the PK table. The exception is when you need to show the role. This will become clear in the Data Model.
.
25. You have no Declarative Referential Integrity, no Defined Foreign keys. That is bad news for many different reasons. Once these questions are clairified, please add them in. DRI means that as much as possible, if not all, Integrity is Declared in SQL. ISO/IEC/ANSI SQL standard allows for this, but the freeware end of the market does not provide the standard, and is slowly catching up. It means the server will not allow a row in the FK table to be added unless the PK exists in the parent table. MySQL recently provided DRI for Foreign Keys. For FKs, refer to ▶this article◀.
25.1. For CHECK constraints and RULES, you will have to implement those in code.
my foreign keys are like, users-id(fk) = users.id(pk) Im not sure how to add them other that what I have done but will certainly do so once I know how to.
Twenty-Five. Comments Noted. I ama not a MySQL specialist. Yes, those are the issues you have to figure out for yourself. In general, from my perusing, MySQL is legless; for anything SQL-ish, you need InnoDB.
.
27. Given: I have rethought the sorting requirements for bulletin. Users could sort chronologically- easy,makes sense. Users could sort bulletins by the date of the latest reply to the bulletin. Then we can forget about rank and it should be really easy to sort bulletins chronologially by the time of their last response? What are your thoughts.
Open Issues
(Nil)
Data Model
Ok, assuming you do not have issues with the ERD, and implementing all Closed Issues, I have modelled the data, and prepared a Fifth Data Model 09 Dec 10 for your review. I definitely need much more feedback, questions, etc, on this. I am experiencing difficulty accepting that it is done. Probably best to start writing real code for your problem areas.
Links
▶Link to IDEF1X Notation◀ You really need to read and understand this, before you read the Data Model.
▶Link to Fifth Bulletin Data Model◀ The Entity Relation Diagram is on the first page, followed by the Data Model.
The Keys are pretty much straight IDEF1X (except for UserId which I provided as a counterpoint); which means purse Relational Keys. Un-enhanced and not optimised for Physical considerations. Before you baulk at them, first notice them, register them, and evaluate them. Of course we can add
Id
iot keys, but before we do that, let's make sure we understand what we are going to lose.Notice the Identifiers (solid lines) as per the Notation document. The spine, the vertebrae of the system is
Location ... Bulletin ... Response
.Notice that Keys actually implement many Business Rules.
Notice the Natural Hierarchy that I have rendered. See if there is any meaning in it for you.
The VerbPhrases are really important; see if they mean anything.
Comments re First Data Model and Responses
One question I have is that the primary key of the location will be used to form the child primary key?(they are joined by a solid line) I don't really understand that concept
or why that relationship does not exist between the user and the bulletin?
As per intention stated further above, since I have now shown Rating as a table and what the rendering would be, once, I shall remove it
I think Permission should be an Entity.
Bulletin
PK is now(StateCode, Town, UserId, SequenceNo)
. To be clear,SequenceNo
is withinStateCode, Town, UserId
: it will be 5 for Sally's 5th bulletin re MO/Billngs FO.Note that user Settings
BulletinsPerPage
,etc, are 1::1 withUser
, so they are inUser
; child table would be incorrect.Typographical errors corrected.
Comments re Second Data Model and Responses
Bulletin
andResponse
have been changed to reflect (7).BulletinNo
andResponseNo
have been replaced withBulletinDate
andResponseDate
(which used to beCreatedDate
), in order to allow multiple replies perUser
perBulletin
.Comments re Third Data Model and Responses
Trust you had a good break.
At least 30 years ago (that I am aware of), the giants in the industry had this debate. Names are always singular. Tables are nouns. VerbPhrases are verbs. This is not limited to db naming conventions, it applies to documents, theses, dissertations, etc. You may have 5 conclusions at the end of the the doc, but the section or chapter title, in both the ToC and the top of the page is "Conclusion".
After fighting them all the way through Uni, as soon as I started my first paid programming job, and saw the importance of the rules in the real world, as opposed to the theoretical arguments we had in college, I gave it up as a waste of time. All that time and energy I wasted was released to do productive work. Since then, I don't question the giants; I just accept. That their minds are greater than mine. It is like accepting Standards, or behaving within the law, or God. I have no really, really good reasons for doing anything illegal.
Anyway, the ease of languaging (discussion, SQL, documentation) that is supported by such rules cannot be adequately explained; as you write more and more SQL code, it will become clear.
You are always free to use whatever you want. I deliver singular only.
Fine with me.
But you need to keep in mind, those two elements, in the identified sequence (ala non-PK Unique Index, or Alternate Key) are universally required to establish Uniqueness for a Person. Removing them will result in two things. First, you will no longer be able to identify uniqueness across
Users
(and thus you may have duplicate rows). Second, the AK becomes non-unique, an Inversion Entry.The point is (contrary to one of the posts), any column that is 1::1 with the
User
PK, should reside inUser
. All preference settings. Since we cleaned up theInterestedLocations
andInterestedCategories
, I know only of onlyBulletinsPerPage
remaining; but I am sure there are others.IsPreference2
is an eg. of a boolean;NumPreference3
is an eg. of an Integer. Etc. You can tell me what the real Preferences are.(Let's try that in plural: ... any column that is 1::1 with the
Users
PK, should reside inUsers
. Just doesn't do it for me, I get hung up on the broken English, and I am a bit precious about my mother tongue.)Data Model Updated.
Excellent. Let me know when you are comfortable with that, and I will give you the Physical Model.
How about the VerbPhrases ?
Comments re 06 Dec 10 20:38 EST (Small Updates)
.
28. Where there is only one occurrence of PK as an FK, of course, the FK column name is the same as the PK column name. However, when there is more than one occ of the FK (take a look at
ResponseRating
), there are threeUserIds
), we need to differentiate them. In IDEF1X terminology this is called Roles. The Role of theUser
who issued theBulletin
isIssuer
, and so on. Obviously it is better to use that name, and keep it consistent throughout the hierarchy (notUserId
inBulletin
and then when we get toResponse
, where there are two, and a differentiation is demanded, change it toIssuerId
. I thought you might have a problem with that; in the early stages, the usage isIssuer.UserId
so that it is absolutely clear the it isUserId
as an FK, and the Role isIssuer
; when we get to the physical model, it gets simplified toIssuerId
.Likewise, we have many DateTime columns (Date for short if you like; otherwise Dtm), that need to be differentiated.
.
29. Did the IDEF1X Notation doc not make sense ?
For
Bulletin
:(StateCode, Town)
for which it is IssuedUserId
of the IssuerTo delete all
ResponseRatings
for thisBulletin
, useWHERE =
on those fourBulletin
columns..
30. Because
(State, Town)
is the PK ofLocation
, carrying wherever. And it forms part of theBulletin
PK, so any dependent tables carry those columns because they are carrying theBulletin
PK.We had previously identified that
(State, Town)
is the PK,I will leave that as isRefer to (38) for change..
33. Worth discussion. Yes, if you are going to display it when (eg) displaying
Responses
, and the users understandUserName
. No, if it is 30 bytes, and there is also an unique 4 byteUserId
. The idea is to make these choices consciously, aware of what you are giving up, when you eventually decide that some 6 column 30-byte key is too cumbersome to migrate to the children.UserId
as a typicalId
Pk, because it is carried/migrated to several child tables..
34. No problem.
Category
already has it. I'll changeOrder
toListOrder
..
35. Sure. Based on what I have read and heard, I am quite happy with it. But I would like more back-and-forth to achieve some confidence, before you write code. Alternately, view it as a learning experience, and accept that the model and code may change later. Would you like me to produce the Physical now ? If you give me any and all corrections, I will publish the next version. I am expecting preferences in
User
. Also, quickly run through the functions and check that you have all the columns you need.Do look at some of the other answers, for the purpose of learning, and interest.
.
36. Joins. You just join on
fourthree columns as opposed to one. SQL is cumbersome with joins, and the new syntax which was supposed to make it easier, is actually more cumbersome. My coders never write joins: we save time and typos. I have a proc that given two or more tables, will generate the code with all the columns and joins. I don't know enough of MySQL to convert that for you.Data Model Updated.
.
Comments re 08 Dec 10 20:49, Fourth Data Model and Responses
.
Check the previous section immediately above, there are small updates.
IDEF1X: Your speed is fine.
Note the child always "inherits" the Parent PK, as an FK (either solid or broken line), otherwise there is no Relation between them. By using these columns that exist in the child anyway, to form the child PK, we carry the meaning (and that is the difference between solid and broken). And thus we do not need to look for an independent Identifier for the child. The Relational power in this method will become clear later, when you are coding.
The section we are dealing with is about Identifiers: natural vs unatural; meaningful vs meaningless. Later you will see how we can use the Relational capability of the engine, when the child PK is formed from the parent PK. (Isn't your surname the same as your father's ?)
It is also important to understand Relational databases and their capability. That is lost when we approach the database (eg) from an OO perspective, and treat it as a location to make our classes "persistent". Therefore, we will try to learn and use Relational terms. It gets difficult when you go to France and expect that they speak American, and use the same currency; learn to speak 10 words of French, and they welcome you with open arms, and you'll have quite a different experience with the locals.
Anyway, go ahead with implementing the model. Just realise we will probably make a change at some point. Save all your DDL. Save all your test data as insert statements or as a table backup or character format export (no idea what MySQL can/cannot do in this area).
.
37.1. Handled, the n::n Relation with
Office
&Category
. You will only "see" that when we get to the Physical Model.37.2. Done.
37.3 Done.
.
38. Excellent. Shorter as well. Note they will never be able to have two
Offices
in the same Zip Code. NUMERIC(5,0) is good, but I thought the US was moving towards 7 digits. Doesn't matter, you can figure it out; it is an excellent PK forOffice
. Now this column, which was part ofAddress
, probablyZipCode
, has been elevated to a higher purpose, without duplication; since we are carrying it in 5 child tables, and we want the PK name to be clear, as per previously explained conventions, we will call itOfficeCode
;OfficeZipCode
might be silly.We need an Unique Index on
Name
to ensure they do not add twoOffices
with the same name. Note, for explanation purposes, this is is actually the logical key ofOffice
, replacing(StateCode, Town)
, and it remains so.I still think you may need
StateCode
andTown
as a quick reference (other than sitting somewhere inAddress
)Data Model updated, Fifth now available for review. You did not state your preference, for
...Date
vs...Dtm
. I am going with the latter, as it is more spceific, identifying the time component as well. Easy to change.This Answer has reached maximum length. Continued in "Part II"
拥有高效数据库的关键是简化。关系数据库的主要目标是不重复任何信息。据我所知,我获取了您的 SQL 转储并快速起草了一个标准化的更简单版本。我确实为简历等留下了一些字段。我删除了一些字段,当需要信息时,通过查询数据库来重新计算会更简单,例如用户的帖子总数和给定帖子的排名。我还删除了您的 bb_replies,因为您可以通过引用父帖子来实现相同的结果。我已将表格稍微重命名为对我来说有意义的名称,您可以使用您觉得舒服的命名方案。我发现使用简单的术语可以更轻松地理解数据之间的关系。
我必须承认我确实同意上面的一些评论,有很多 BB 可以很好地工作并且具有您正在寻找的所有功能。你很幸运,我今晚有读书的心情,哈哈,这是一个很长的问题。简化是一切的关键:)
They key to having an efficient database is to simplify. The main goal of a relational database is not to repeat any information. I took your SQL dump and quickly drafted a simpler version that is normalized, to the best of my knowledge. I did leave some of the fields you had in for cvs's ect. I have removed fields that it would be simpler to just recalculate by querying the db when the information is needed, such as a users total posts and a ranking of a given post. I also removed your bb_replies as you can accomplish the same result with referencing to a parent post. I have renamed the tables slightly to what made sense to me, you can use what ever naming scheme you feel comfortable with. I find that using terms that are simple makes it easier to understand how the data relates to each other.
I must admit that I do agree with some of the comments above, there are plenty of BBs out there that work just fine and would have all the functionality you are looking for. And you are lucky I am in the reading mood tonight lol that was one long question. Simplification is key in everything :)
首先是子查询,然后是 RANK() 函数
放轻松,儿子,我们会成功的!你的速度没问题。
准备
首先,您确实需要获得一套适合您的 MySQL 特定风格的手册。我找到了▶这个◀。和以前一样,您必须自己进行调试,但我现在提供尽可能接近通用 MySQL 的 SQL。我已经确认,我们要做的一切在那种风格的 MySQL 中都是完全可能的(我不知道你的风格/版本是什么,除了 ENGINE=MyISAM)。
子查询
好吧,让我们重新开始。我写了▶系列SELECTS◀ ,引导您完成整个过程。请完成每一项,并在进行下一项之前完全理解它。如果您有任何疑问,请停止并发布问题。
代码在Sybase中编写和测试;然后降级 MySQL(通过浏览网络,例如上述站点),并在该状态下进行尽可能多的测试。
第一个位创建并加载三个表以供使用。
第一个 SELECT 是三个表的直接连接,没有子查询。你需要让它发挥作用;也就是说,理解它的作用,修复任何语法问题;找出我提供的 SQL 和您服务器上运行的 SQL 之间的差异。并习惯于做出这些改变。我们不能一直为此停下来。
第二个 SELECT 产生完全相同的结果集。它引入了子查询的概念,用于填充单个列。
驾驶那辆公共汽车。完成后或遇到问题时进行回复。
评论的回复
我以前从未见过这种连接方式,我一直使用左连接、右连接或内连接。好的,对于第一个查询,我们只是将学生和课程这两个表与位于中间的学生课程表作为关联表连接起来。结果会按预期重复,因为一名学生可能上了一门以上的课程,并且他们将获得该课程的结果。
(ps,当我使用您提供的查询直接创建表时,名称将转换为全部小写,而列名称仍然可以是驼峰式大小写。)
。
2. 简单标量查询
查询的一些问题。在定义别名之前,您使用别名(在标量子查询中)吗?(StudentCourse sc)我想我总是错误地认为您必须在使用别名之前先定义别名。
我不完全理解标量子查询中别名“in-ner”的使用,这是否是说您希望它单独检查每一行(不知道如何解释这一点)而不是在表上宽检查?即,当您进行此检查时,使其位于您所在的特定行本地?(抱歉,解释很糟糕)。
in_ner
和sc
是别名,即FROM
子句中紧邻的表名的句柄;为了方便起见,我们在代码中的其他地方使用in_ner
是内部查询中引用的表的描述性名称,子查询sc
是在内部查询中引用的表的描述性名称外部查询,它只是外部,因为它有一个内部查询,否则它将是一个平面查询,fred
和sally
当内部查询和外部查询中引用同一个表时,in_ner
和out_er
才有意义。WHERE in_ner.CourseId = sc.CourseId
in_ner
查询中引用的表到 Out_er 查询中引用的表 sc据我了解,标量子查询要求名称,其中 Course 和 Studentcourse 中的 courseId 相同。(非常简单),并且是在 where 中表示的另一种选择,
因此需要内部子查询和外部(特定行)之间的关联。
如果我们没有特定行的标识,我们就会将垃圾加载到标量中,或者它将返回一个表(而不是标量值)并且查询将失败。
WHERE CourseId = sc.CourseId
取出WHERE”中遗漏了一些东西 子句;我没有识别特定的相关行”。
。
sc
行。不同的是,您可以在 where 之前逐行进行此检查。
我使用了 Course 或者 in-ner,在这种情况下使用别名有什么意义,只是为了表明可以使用别名吗?
我不明白的是,当我尝试执行此操作时,“course.Name”在字段列表中显示未知的 Course.Name。这是我一直定义的方式,我的意思是课程表中的名称,而不是其他表中的名称。如果我有两个带有名称列的表会发生什么?
我也从未见过这种 order by 语法,我可以看到 1 和 4 表示列号,但为什么要费心传递两列呢?
将
ORDER BY
拿出来玩玩。尝试
ORDER BY 4 DESC, 1
2.1.好的,当您完成(2)并且非常高兴您理解它时,请做这个练习。
生成相同的网格格式,我们想要与 (1) 和 (2) 完全相同的结果集。
用适当的子查询填充两对空括号; IE。编写一个子查询来填充 FirstName 列,再编写一个子查询来填充 LastName 列
对您关于第三数据模型
2.1 的评论的响应。完美,是的,我们继续前进。
.
你正在用煤气做饭,所以如果你不介意的话,我会记下你的文字,并做一些注释;注意这些差异,它们可能很微妙,也可能不那么微妙。
我自己说得更好。
不存在“结果表”这样的东西。
添加一项更明确的项目。
外部查询定义结果集。
好的,那么 SQL 就可以工作了,对吗?
现在您已经了解了这一点,下一步是可视化结果集,并可视化填充整个列的子查询(3,未更改)。如果上面的文本是一个气球,一次填充一个单元格,那么想象数百个气球,填充连续的单元格。然后想象一个桶倒入柱中。
现在,将二维结果集单独放置一分钟,并可视化其顶部的另一层。这是并行层,您可以在其中编写子查询代码。
如果您在使子查询工作时遇到困难,请返回到您的可视化方式,一个结果集和子查询的另一层,该子查询倒入一桶标量以填充列。它消除了所有众所周知的子查询编码错误;删除了 GROUP BY、DISTINCT 的使用,以及所有那些让一条愤怒的长蛇装入果酱罐的笨方法。
.
在继续 (4) 之前,还要执行三个小步骤。
2.2 重新阅读我上面的回复(2),一直读到这里。没有略读。这是因为当你教给你的大脑一些新的、不同的东西时,你需要强化它。这是一种官方认可和标记的技术。
对 2010 年 12 月 8 日 20:49 评论的回复
2.3。在不使用子查询的情况下编写该查询 (3),并确保检查结果。如果您在编写代码时发现自己在笑,这是一个好兆头。只要生成正确的结果集,就可以通过,但请尝试编写最有效的代码(最少的 COUNTS 和 GROUP BY 等)。仅当您想在同事周围转圈,以便能够回答数据库中的任何“我如何编码......”问题时才执行此操作。
2.4.在您的数据库上编写子查询(3),以生成
公告
列表,外部查询必须仅为FROM bbs
;包含喜欢
计数和不喜欢
计数。因此,截断表并执行 10 或 12 次有意义的插入,时间很长,很重要。回复2010年12月11日13:14评论
(2.3) 练习的目的是让您:
真正理解使用GROUP BY(在使用集合处理关系引擎的关系数据库中)与相关子查询的正确性、优雅性和速度。你已经做到了。这将使您在 SQL 编码能力方面优于同龄人。
能够识别何时使用胖 WHERE 子句以及何时使用相关子查询是合适的。我不确定,但看起来您已经实现了这一点。
在维护他人编写的代码时能够纠正和调试此类问题,并能够教他们区分。听起来你有很好的视觉、关系能力;通过练习得到了强化;现在你不能再回到劣等的方法了。也就是说,您可以理解并修复不正确的 SQL 代码,但无法将其传达给其他人。
只要您理解这些区别并接受这一点,我很乐意放弃(2.3)并继续前进。
再次阅读您的 (2.4) 查询,以刷新自己,然后继续 (4)。
不要继续阅读。以下是“旧代码”
我从未见过这种写 Rank = (SELECT...) 的方法与 Rank 的 (SELECT ...) 相同吗?
in_ner
和out_er
是别名,即FROM
子句中紧邻的表名的句柄;为了方便起见,我们在代码中的其他地方使用in_ner
是内部查询中引用的表的描述性名称,子查询out_er
是在内部查询中引用的表的描述性名称外部查询,它只是外部,因为它有一个内部查询,否则它将是一个平面查询,fred
和sally
in_ner
查询中引用的表与out_er
查询中引用的表相关FROM
子句中的bbs
和bb_locations
,联接通过WHERE
子句或JOIN
语法)。我不明白内部和外部,当我用谷歌搜索它们时,我得到 INNER JOIN 他们叫什么,这样我就可以进一步研究
当我运行该 select 语句时,我收到此错误 您的 SQL 语法中有错误; 检查与您的 MySQL 服务器版本相对应的手册,以了解在第 5 行的 'WHERE inner.Mark >=outer.Mark ) FROM Studentmark external ORDER B' 附近使用的正确语法
inner
和outer
可能被视为保留字。所以按照上面的代码更改它。Subquery First, then the RANK() Function
Relax, son, we'll get there! Your speed is fine.
Preparation
The first thing, you really need to get access to a decent set of manuals, for your specific flavour of MySQL. I found ▶this one◀. As before, you have to do your own debugging, but I am now providing SQL that is as close to generic MySQL as possible. I've confirmed that everything we are going to be doing is entirely possible in that flavour of MySQL (I don't know what flavour/version yours is, except ENGINE=MyISAM).
Subquery
Ok, let's start again. I have written a ▶series of SELECTS◀, to lead your through the process. Please complete each one, and understand it completely before progressing to the next. If you have any questions, stop, and post the question.
The code is written and tested in Sybase; then downgraded for MySQL (from perusing the web, eg. the above site), and tested as much as possible in that state.
The first bit creates and loads three tables for use.
The first SELECT is a straight join of the three tables, no subquery. You need to get that to work; that is, understand what is does, fix any syntax problems; figure out the differences between the SQL I provide and the SQL runs on your server. And get used to making those changes. We can't keep stopping for that.
The second SELECT produces exactly the same result set. It introduces the concept of a Subquery, which is used to populate a single column.
Drive that bus. Respond when you're done or if your have problems.
Responses to Your Comments of 03 Dec 10 17:51
I have never seen that way of doing joins before, I have always used left join, right join or inner join. Ok so for this first query we are just joining the two tables student and course with the studentcourse table sitting in the middle as the associative table. Results are repeated as expected because one student might be on more that one course and they will have a result for that course.
(ps when i direclty create the tables using queries you provided, the names are converted to all lowercase while the column names can still be camel case.)
.
2. Simple scalar query
A few issues with query. You use the alias(in the scalar subquery) before you have defined what it is?(StudentCourse sc) I guess I always incorrectly assumed that you have to say define an alias before you use it.
I don't entirely understand the use of the alias 'in-ner' in the scalar subquery, is this to say that you want it to check each row individually(not sure how to explain this) instead of on a table wide check?Ie when you are doing this check make it local to the particular row you are on?(terrible explanation sorry).
in_ner
andsc
are ALIASES, that is, handles for the table name that it sits next to in theFROM
clause; that we use elsewhere in the code for conveniencein_ner
is a descriptive name for the table referenced in the Inner Query, the Subquerysc
is a descriptive name for the table referenced in the Outer Query, which is only Outer because it has an Inner query, otherwise it would be a flat queryfred
andsally
in_ner
andout_er
are meaningful when the same table is referenced in both the Inner and Outer queries.WHERE in_ner.CourseId = sc.CourseId
in_ner
query to the table sc referenced in Out_er queryAs i understand it the scalar subquery asks for Name where the courseId's in Course and studentcourse are the same.(pretty straight forward) and is an alternative to saying that in the where,
so the Correlation between the Inner Subquery and Outer (specific row) is required.
And if we did not have that identification of the specific row, we would be loading rubbish into the Scalar, or it would return a Table (not a Scalar value) and the query would fail.
WHERE CourseId = sc.CourseId
outWHERE
clause; I am not identifying a specific Correlated row"..
sc
row it is.with the differnece that you can make this check row by row before the where.
I used Course instead or in-ner, what is the point of using an alias in this case, is it just to show that aliases can be used?
Something I don't understand here is that when I try to do this, 'course.Name' it says unknown Course.Name in field list. this is the way that I have always defined that i mean Name in the Course table and not some other table. What would happen if I had two tables with a name column?
I have also never seen that order by syntax, I can see that 1 and 4 mean the column numbers but why bother passing it two columns?
Take the
ORDER BY
out and play with it.Try
ORDER BY 4 DESC, 1
2.1. Ok, when you finished with (2), and completely happy that you understand it, do this exercise.
Produce the same grid format, we want the exact same result set as (1) and (2).
Fill in the two pairs of empty brackets with the appropriate subquery; ie. write a subquery to populate the FirstName column, and another to populate the LastName column
Responses to your Comments re Third Data Model
2.1. Perfect, yes, we move on.
.
You are cooking with gas, so if you don't mind, I will take your text, and annotate it a bit; notice the differences, they may or may not be subtle.
I could hardly have said it better myself.
There is no such thing as "result table".
Add one more definitive item.
The outer query defines the result set.
Ok, so you have that SQL working, right ?
Now that you understand that, the next step is to visualise the result set, and to visulaise the subquery (3, unchanged) filling the entire column. if the above text was a balloon filling one cell at a time, then visualise hundreds of ballons, filling consecutive cells. Then visualise a bucket poured into the column.
Now leave that two dimensional result set alone for a minute, and visualise another layer on top of it. This is the parallel layer, where you write your subquery code.
If ever you have difficulty getting a subquery to work, go back to this, your way of visualising, one result set, and another layer for the subquery, which pours a bucket of scalars in, to fill the column. It eliminates all the well-known subquery coding bugs; removes the use of GROUP BY, DISTINCT, and all those ham-fisted methods of getting a long angry snake to fit into a jam jar.
.
Three more small steps before you proceed to (4).
2.2 Re-read my response (2) above, all the way down to this point. No skimming. This is because when you teach your mind something new and different, you need to re-inforce it. It is an officially recognised and labelled technique.
Responses to Comments of 08 Dec 10 20:49
2.3. Write that query (3) without using subqueries, and ensure you check the results. If you catch yourself laughing when you are writing the code, it is a good sign. As long as you produce the correct result set, you pass, but try to write the most efficient code (fewest COUNTS and GROUP BYs, etc). Do this only if you want to run circles around your peers, to be able to answer any "how do I code ..." question on your database.
2.4. Write the subquery (3) on your database, to produce a list of
Bulletins
, the outer query has to beFROM bbs
only; with a count oflikes
, and a count ofdislikes
. So trunacte the tables and do 10 or 12 meaningful INSERTS, fibe minutes, big deal.Responses to Comments of 11 Dec 10 13:14
The (2.3) exercise is intended for you to:
really understand the incorrectness of the fat query with the
GROUP BY
(in a relational database using a set-processing relational engine) vs the correctness, elegance, and speed of the Correlated Subquery. You have achieved that. That will place you above your peers, in terms of SQL coding ability.be able to identify when a fat WHERE clause and when a Correlated Subquery is appropriate. I am not sure, but it looks like you have achieved that.
be able to correct and debug this kind of issue when maintaining code written by others, and to be able to teach them the distinction. It sounds like you have a good visual, relational ability; which has been re-inforced by the exercise; and now you cannot go back to inferior methods. That is, you can understand and fix incorrect SQL code, but you cannot communicate that to others.
As long as you understand those distinctions and accept that, I am happy to drop (2.3) and move on.
Read your (2.4) query again, to refresh yourself, and move onto (4).
Don't read further. The following is "old code"
I have never seen that approach of writing Rank = (SELECT.... Is that the same as (SELECT ...) as Rank?
in_ner
andout_er
are ALIASES, that is, handles for the table name that it sits next to in theFROM
clause; that we use elsewhere in the code for conveniencein_ner
is a descriptive name for the table referenced in the Inner Query, the Subqueryout_er
is a descriptive name for the table referenced in the Outer Query, which is only Outer because it has an Inner query, otherwise it would be a flat queryfred
andsally
in_ner
query to the table referenced inout_er
querybbs
andbb_locations
in theFROM
clause, joining via theWHERE
clause orJOIN
syntax).I don't understand inner and outer, when I google them I get INNER JOIN what are they called so I can research further
When I run that select statement I get this error You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE inner.Mark >= outer.Mark ) FROM studentmark outer ORDER B' at line 5
inner
andouter
are probably being treated as reserved words. So change that as per the above code.第二部分
是第一部分的延续,因为该答案已达到最大长度。
2011 年 1 月 14 日 - 05:40 PST 评论修订
于 2010 年 12 月 11 日 13:14,第五数据模型和
响应IDEF1X 设计/绘图工具。
我不知道有任何免费软件选项。据报道,MySQL 设计工具经常崩溃。如果您对我的图表感到满意,我很乐意在这段时间内与您合作,直到最终模型得到解决; IE。我提供了数据模型,您可以跳过该任务。对于正在进行的工作,是的,您需要一个图表工具,也许不是数据库设计工具。请参阅 Notation 文档中 p2 末尾的我的评论。
.
29. 根据第五数据模型中的彩色选项卡,您清楚每个表中的 PK 和 FK 吗?我现在可以删除标签吗?
.
38. 关闭。
.
39. 所有
Dtm
列都将是MySQLDATETIME
数据类型。您用于这些列的变量应该相同。TimeStamp
有不同的含义。使用正确的数据类型是确保数据正确并且不允许非法值进入数据库的第一步(重要)。即,仅允许有效的日期和时间。此外,您可以从中查询任何日期或时间部分(例如月份或日期名称)。检查此文档。< br>.
40.没问题。我们不只是硬编码一个类别(例如处理权限),而是实现一个
Category.IsRestricted
,然后Permission 5
变为Post Restricted公告
。.
41. 完成。
您应该考虑对
Category
和User
执行相同的操作。您希望能够通过设置指示器来删除它们,而不从数据库中删除条目(以及所有公告、响应、回复
等)。它必须保留用于历史目的,但您需要禁止用户登录并执行任何操作。我已将其包含在 DM 中。对于此类列名和布尔值,我个人更喜欢识别少数或异常情况,如
IsObsolete
中所示。20.2.完毕。表和列命名现已发展为 InnoDB 格式。
.
子查询答案中的子查询响应。
评论 2010 年 12 月 13 日 13:14 美国东部时间和回应
。
41. 参见上文 (41) 和下一段。
.
42. 我的意思是:
Title
或Description
就足够了;我们两者都不需要。.
43.1。数据模型的实现。前进。这就是我昨天给你体检的原因。
.
43.2.设计/绘图。前进。我已经在上面(a)中评论过。
数据模型
第六种数据模型,包含上述所有更改。
物理意味着实现/编码所需的更多细节:数据类型; n::n 关系作为关联表实现;等等。您已经准备好实现数据模型,这意味着您需要物理模型。您已经弄清楚了关联表。因此,尽管您说您不着急,但我还是冒昧地向您提供了物理 DM。
请注意,域(用户定义的数据类型)应始终在数据库中使用,两者都用于 DDL;您使用的 $variables。每个主键都有一个私有域。但这在 MySQL 中是不可能的,因此遗憾的是,数据类型是原始的。
固定长度列比可变长度列快得多;我不提供(建议)Var 长度。您可以自由地实现您喜欢的内容。
您确定需要
Category.Title
和Description
吗?我想不会,但我已将其保留在您确认之前。享受蓝色玻璃小按钮以及折叠实体中的导航。
请再次阅读IDEF1X Notation文档,我上周对其进行了扩展。
根据未决问题的关闭方式以及您可能遇到的任何问题,我们可以在第二天/晚上进行另一个版本。
评论 28 Dec 10 10:34 和回复
我已经开始实施数据模型。我假设第六个数据模型是物理模型,因为它包含关联表。
是的,我提供了它和数据类型,因为您说您已准备好实现。
仍有一些较小的未完成项目。也许是解决你的问题的好时机;我的所有三个答案,然后检查。例如,
Category.Title
和Description
。一旦完成,我将建立一个数据库转储。
这是没有必要的,因为模型已经定义了数据类型;但如果你确实发布了,我当然会帮你检查。电子邮件可能会更好。
然后我将列出我需要在数据库上运行的所有查询的列表并开始编写它们。
非常好的主意,采用结构化和有计划的方法来完成这项工作。
物理模型的实现
(39) 使用 mysql,我无法分配多个主键,因此我将按照您在文档中建议的那样使它们唯一且不为空。您认为对它们也建立索引是个好主意吗?
不确定您的意思,什么是“它们”?:
评论 07 Jan 11 14:08 和回复
(40) 您能解释一下为什么category.CategoryCode 是一个 4 个字符的字符吗?为什么不像我们为用户那样使用数字?
40.1。这个想法是使用良好的自然标识符。数字对于用户来说毫无意义。如果我们没有大量的用户和用户流失,我也不会在那里使用数字。 CHAR(2) 或 (3) 或 (4) 允许他们为长 Category.Description 选择有意义的短代码,并且它足够小,可以作为
user_category
和 <代码>公告。对于开发人员来说,在测试和调试时,
公告
列表中的短代码将非常方便。(40) 我不太了解类别和位置的权限。假设我希望所有用户都能够向“旅行”类别发帖。我要把这个类别的权限设置为4吗?为什么我们在授予类别和位置权限时需要bool IsRestricted?
40.2.我没有改变Permission的概念或本质;这是你的想法,而且和你向我解释的一模一样。
(我所做的只是将
permission
实现为一个表。)40.3。参见(13)和(40)原始交换。
category.IsRestricted
定义受限类别;有两类categories
:受限制的和不受限制的。用户需要权限
为 5 才能发布有关受限制类别的公告,并且需要拥有 4 权限才能发布有关不受限制类别的公告。40.4。但是哇,儿子,你是在引入改变或扩展吗?例如。将
user
的permission
与category
的permission
相匹配,从而允许多于两个类别的<代码>类别?请不要。这意味着权限
对于类别
意味着一件事,而对于系统的其余部分则意味着另一件事。或者,如果您这样做,那么我们必须首先解决确切的需求,然后将其作为更改实施。40.5。该位 (40.2) 和 (40.3) 的位置(现在的
办公室
)完全相同。如果你指的是底部的文字,那是我的小错误,我会纠正它。40.6。根据 (14),
office
另外还有一个AdministratorId
(UserId
)。在模型上显示为(允许的)用户
。40.7。但这带来了一个问题:谁可以管理
类别
?现在任何人都拥有 5 或更高的权限
,这是另一回事。我认为我们需要一些明确的东西,权限
6 = 管理类别。其他
已处理您的 DDL 并返回。
数据模型已更新。修正了一些小的澄清和两个小错误。
评论 08 Jan 11 14:08 和回复
(我认为那是 1 月 9 日,而不是 1 月 8 日...我确实检查了更新。)
(40) 我不太明白类别和位置的权限。我没有改变任何东西。请忽略上一个问题的内容,因为请解释将设置什么权限以允许用户向“旅行”类别发帖。在我的实现中,我只有一个权限列。如果给定用户拥有所需的权限或更高权限,那么他们就可以向某个类别发帖,这就是新系统的工作原理吗?
是的。不变。
category.permission
与此无关。他们需要user.permission
4 来实现不受限制的类别
。如果
category.IsRestricted
,他们需要user.permission
5.完全不同的是,用户需要任何
类别的
是为了管理user.Permission
。权限类别
。请勿使用小于 4 的值。(41) 查询删除公告及其所有相关回复和评级。
我没想到,您确定他们不需要保留所有过去的公告作为历史记录吗?或审计目的?
无论如何,让我们在允许删除公告的基础上处理这个问题......
我什至不知道从哪里开始处理这个问题。过去,我加入的是将公告表与响应表、响应表与响应评级表以及公告 id = x 的公告评级表连接起来,然后将其删除。但现在任何一个特定的公告都由三列标识:OfficeCode、IssuerId 和 BulletinDtm。它们作为外键传递到子表。首先,我如何在我的 php 中存储要删除哪个公告?通常我会有一个像这样的链接
index.php?action=delete&bulletin-id=5
。现在我必须有一个链接index.php?action=delete&OfficeCode=20001&IssuerId=34&BulletinDtm=14:02...
我真的不知道如何做到这一点?我无法帮助你,我是数据库和 SQL 专家,而不是 php 或 MyISAM 专家。您需要将其作为新问题发布在 SO 或 MySQL 板上。
b.就我对该主题的阅读而言,我不知道语法,但是是的,它可以完成,这是正常的。我在向您推荐复合键之前检查过。更正的 DDL 成功,并且索引已确认,对吗?
c.问题只是复合键或复合键所需的语法;并使用
index.php
。类似:index.php?操作=删除& OfficeCode=x 和 IssuerId=y 和 BulletinDtm=z
d.为什么不能使用 mysql_query 而不是
index.php
并因此使用完整的 SQL ?据我了解,它与 MyISAM 一起使用。然后你可以使用:e.先删除
response_ rating
;然后响应
;然后bulletin_ rating
;然后是公告
。当他们切换到 InnoDB 时,他们要做的更改就会减少。f.最重要的是,您必须让他们确定删除公告的依据。任何及所有公告均不应删除。类似“一年没有活动”或“关闭”(这意味着添加了一个列)等。
评论 10 Jan 11 14:08 EST 和响应
(41.10-Jan-11) 没问题,方法很好,我有需要在上面 (41.*) 下解决的详细相关问题。 (41.f)还需要答案……除了权限之外,删除公告还有什么依据吗?
评论 10 Jan 11 13:48 pst 和回复
SO 编辑。别担心,那不是你。该网站的技术质量很差。编辑是无望的(相信我,我已经尝试使用它并围绕它进行工作,以使我的答案看起来有点就像我希望它们出现一样)。它无法正确处理缩进或多级编号。
删除基础。好吧,你有一个有效的基础。并且撰写
回复
的用户不会介意他们在未经询问的情况下被删除吗?(41) 您正在寻找的是标准 SQL 中的“级联删除”,它是在外键子句中定义的(MyISAM 中没有该子句)。每个 INSERT/UPDATE/DELETE 动词仅适用于一个表,可能通过 REFERENCE 影响其他表。
对于非标准 SQL,您可以使用 DELETE multiple_table 方法(非标准语法)。
首先,理解这一点非常重要。 DELETE 命令中的 FROM 和 WHERE(或 JOIN)子句与 DELETE 本身是分开的;它们实际上是一个 SELECT。想法是:DELETE table_one (SELECT FROM table_one, table_two WHERE join_conditions)。
因此:
bulletin
PK)公告
复合主键。这是DELETE 语法 和 JOIN 语法。
2011 年 1 月 12 日 21:48 评论和
回复不要偷懒。自下而上编写四个删除语句。这就是我们必须在城市的尽头做的事情,在那里我们没有“级联删除”。为 rating_response 编写删除;然后复制粘贴,每次删除一行代码。我不明白这种焦虑或回避。
b.我再说一遍,不要使用左、右或任何类型的外连接(这仅是单个全包删除所需的)。仅使用直接内连接(这对于 4 个删除语句来说不是问题)。您遇到的任何烦恼都是由于您需要使用一次删除而导致的。放弃它,烦恼和并发症就会消失。
SELECT
命令。三列PK乘以四个子查询。我不明白需要避免长命令(再次要求,因为 SQL 很麻烦)。我什至没有使用 JOIN 语法。我花了十分钟来写,再加上五分钟来测试。究竟是什么,有什么大不了的?c.您没有忘记关系键的力量,几周前您就认识到了,不是吗。例如。能够从 rating_response 获取公告,而不必加入评级。如果你屈服于单列键的欲望,你将失去这一切。 SQL 很麻烦。但这就是我们所拥有的一切。处理它。非 SQL 试图“让生活变得轻松”,但事实上,引入了各种不必要的和可以避免的复杂性。举个例子。
2011 年 1 月 13 日 21:18 PST 的评论和回复
删除。三种口味。伟大的。希望您的数据值位于 $variables 中,这样就不会出现这种形式的重复。对于测试来说,这很好。
删除 x 四个表。 (不是“单独删除记录”,这是完全不同的事情;除了最后一个之外的每次删除都可能会删除数百行)。我相信剪切和粘贴只需要几秒钟。您需要小心,不要忘记更改表名称。
单个删除命令。第一个三元组的 $variables。您可以在除第一个三元组之外的所有列中使用列名称。
好的,测试后您将把 SELECT 转换为 DELETE。左连接。对于单个删除是必需的,但其他情况下则不需要。这与 (2) 相同,只是将 WHERE 替换为 JOIN>
我已经推荐了(1),但你更有可能选择(3)。
Part II
Continuation of Part I, due to that Answer reaching maximum length.
Revised 14 Jan 11 - 05:40 PST
Comments re 11 Dec 10 13:14, Fifth Data Model and Responses
a. IDEF1X Design/Diagramming tool.
I do not know of any freeware options. The MySQL design tool reportedly crashes often. If you are happy with my diagrams, I am happy to work with you for the duration, until the final model is resolved; ie. I provide the Data Model, and you can skip that task. For ongoing work, yes, you need a diagramming tool, perhaps not a database design tool. Refer my comments at the end of p2 in the Notation doc.
.
29. Are you clear about the PKs and FKs in each table, as per the coloured tabs in the Fifth Data Model; can I remove the tabs now ?
.
38. Closed.
.
39. All the
Dtm
columns will be MySQLDATETIME
datatypes. The variables you use for those columns should be the same.TimeStamp
has a different meaning. Using the correct Datatypes is the first (big) step towards ensuring that the data is coorect and no illegal values are allowed to enter the db. Ie, only valid dates and times will be allowed. Further, you can interrogate any date or time component (eg. month or day name) from it. Check this document..
40. No Problem. Instead of having just the one category hardcoded, how about (like the handling your of Permission), we implement an
Category.IsRestricted
and thenPermission 5
becomesPost Restricted Bulletins
..
41. Done.
You should think about doing the same for
Category
andUser
. You want to be able to delete them by setting the Indicator, without removing the entry (and all theBulletins, Responses, Replies
, etc) fro the database. It has to be retained for historical purposes, but you need to disallow the User from logging in and doing anything. I have included this in the DM.For such column names and Booleans in general, personally I prefer to identify the minority or exception case, as in
IsObsolete
.20.2. Done. Table and column naming now progressed to InnoDB format.
.
Subquery responses in Subquery Answer.
Comments 13 Dec 10 13:14 EST and Responses
.
41. See (41) and next para, above.
.
42. I meant: either
Title
orDescription
is enough; we do not need both..
43.1. Implementation of Data Model. Go ahead. That's why I gave you the Physical yesterday.
.
43.2. Design/Drawing. Go ahead. I have already commented in (a) above.
Data Model
Sixth Data Model supplied, containing all changes as per above.
The Physical means a lot more detail required for implementation/coding: Datatypes; n::n Relations implemented as Associative tables; etc. You are pretty much ready to implement the Data Model, which means you need the Physical. And you already have the Associative tables figured out. Therefore I have taken the liberty of providing you with the Physical DM, even though you said you were in no hurry.
Note that Domains (User Defined Datatyptes) should always be used in a database, both for the DDL; the $variables you use. And a private Domain for each Primary Key. But this is not possible in MySQL, therefore the Datatypes are raw, regrettably.
Fixed length columns are much faster than variable length; I do not provide (advise) Var length. You are free to implement what you like.
Are you sure you need both
Category.Title
andDescription
? I think not, but I have left it in until you confirm.Enjoy the little blue glass buttons, and the navigation from the Collapsed Entities.
Please read the IDEF1X Notation document again, I expanded it last week.
Depending on how the Open Issues close, and any issues you may have, we can progress another edition, in the next day/night.
Comments 28 Dec 10 10:34 and Response
I have begun implementing the data model. I assume that the 6th data model is the physical model because it contains the associative tables.
Yes, I supplied that, and the Datatypes, because you said you were ready to implement.
There are still a few minor outstanding items. May be a good time to go through your question; all three of my answers, and check.
Category.Title
andDescription
, for instance.I will put up a database dump once I am done.
That is not necessary, given that the model has the Dataypes defined; but if you do post it, sure, I will check it for you. Email may be better.
I will then put up a list of all the queries that I need to run on the database and begin writing them.
Very good idea, to take a structured and planned approach to the job.
Implementation of Physical model
(39) With mysql I am not able to assign more than one primary key so I am just going to make them unique and not null as you suggest in the documentation. Do you think it would be a good idea to index them as well?
Not sure what you mean, what is "them" ?:
AKx[.y]
in the model) must be defined as an additional index, UNIQUE, NOT NULL.Comments 07 Jan 11 14:08 and Response
(40) Could you explain why the category.CategoryCode is a char of 4 characters. Why not just use an number like we do for user?
40.1. The idea is to use good natural Identifiers. Numbers are meaningless to users. If we didn't have a large no of Users, and User churn, I would not have used a number there either. A CHAR(2) or (3) or (4) allows them to pick meaningful short code for the long Category.Description, and it is small enough to be carried as a Foreign Key in
user_category
andbulletin
.For the developer, when testing and debugging, that short code in a list of say
bulletins
, will be very handy.(40) I don't quite understand permission for category and location. Lets say that I want all users to be able to post to the Travel category. Would I set the permission of this category at 4? Why do we need to bool IsRestricted when we are giving a permission to the category and location?
40.2. I have not changed the concept or essence of Permission; it was your idea, and it remains exactly as you explained it to me.
(All I did was implement
permission
as a table.)40.3. Refer (13) and (40) original exchange.
category.IsRestricted
defines restricted categories; there are two categories ofcategories
, Restricted and Unrestricted. The users need apermission
of 5 to post bulletins re Restricted categories, and 4 to post bulletins re Unrestricted categories.40.4. But whoa, son, are you introducing a change or extension; eg. match the
permission
of theuser
to thepermission
of thecategory
, thus allowing far more than two categories ofcategories
? Please don't. That would meanpermission
means one thing recategory
and a different thing re the rest of the system. Or if you do, then we have to resolve the exact need first, then implement it as a change.40.5. Location (now
office
) is exactly the same for that bit (40.2) and (40.3). If you are referring to the text at the bottom, it is my small mistake, will correct it.40.6. As per (14),
office
additionally has a singleAdministratorId
(UserId
). Shown on the model as(permitted) user
.40.7. But that brings up an issue: who can administer
categories
? Right now anyone withpermission
5 or greater, which is a different thing. I think we need something explicit, apermission
6 = Administer Category.Other
Processed your DDL and returned.
Data Model Updated. Number of small clarifications and two minor errors corrected.
Comments 08 Jan 11 14:08 and Response
(I think that was 09 Jan not 08 Jan ... I did check for updates.)
(40) I don't quite understand permission for category and location. I haven't changed anything. Disregard the content of the previous question as explain what permission would be set to allow users to post to the 'Travel' Category. In my implementation I simply had a permission column. If a given user had the required permission or greater then they could post to a category, is that how the new system works?
Yes. Unchanged.
category.permission
has nothing to do with it. They needuser.permission
4 for unrestrictedcategories
.If the
category.IsRestricted
, they needuser.permission
5.Quite separately, an user needs
user.Permission
of whatevercategory.Permission
is, in order to administercategory
. Do not use values less than 4.(41) Query Delete a bulletin and all its associated replies and ratings.
I did not expect that, are you sure they have no need to keep all past bulletins for historical or audit purposes ?
Anyway, let's deal with that on the basis that deleting bulletins is allowed ...
I don't even know where to get started on this. In the past I have joined would have joined up the bulletin table with the response table and the response table with the response rating table and the bulletin-rating table where the bulletin id = x and deleted them. But now any one particular bulletin is identified by three columns: the OfficeCode,IssuerId and BulletinDtm. Which are carried to the child tables as foreign keys. For a start, how do I store indicate which bulletin is to be deleted in my php? Usually I would have a link like this
index.php?action=delete&bulletin-id=5
. Now will I have to have a link that isindex.php?action=delete&OfficeCode=20001&IssuerId=34&BulletinDtm=14:02...
I really have no clue how to do this?a. I can't help you there, I am a database and SQL expert, not a php or MyISAM expert. You will need to post that as a new question on SO or the MySQL boards.
b. As far as my reading of that subject has taken me, I don't know the syntax, but yes, it can be done, it is normal. I checked before recommending composite keys to you. The corrected DDL succeeded, and the indices are confirmed, correct ?
c. The issue is simply the syntax required for composite or compound keys; and working with
index.php
. Something like:index.php? action=delete & OfficeCode=x and IssuerId=y and BulletinDtm=z
d. Why can't you use mysql_query instead of
index.php
and thus use full SQL ? As I understand it, it works with MyISAM. Then you can use:e. Delete
response_rating
first; thenresponse
; thenbulletin_rating
; thenbulletin
. When they switch to InnoDB they will have less changes to make.f. Most important, you will have to get them to identify the basis on which a bulletin can be deleted. ANy and all bulletins should not be deleted. Something like "no activity for one year" or "closed" (which means an added column), etc.
Comments 10 Jan 11 14:08 EST and Response
(41.10-Jan-11) No problem, the method is fine, and I have detailed related issues which need address under (41.*) above. (41.f) still needs an answer ... other than permissions, is there any basis for deleting bulletins ?
Comments 10 Jan 11 13:48 pst and Response
SO Editing. Don't worry, it is not you. The site is of poor technical quality. The editing is hopeless (and believe me, I have tried to work with it and around it, to make my Answers appear even somewhat like I want them to appear). It cannot handle indents or more than one level of numbering correctly.
Delete Basis. Ok, you have a valid basis. And the users who wrote
responses
would not mind if they were deleted without being asked ?(41) What you are looking for is a "cascading delete" in Standard SQL, which is defined in the Foreign Key clause (which you do not have in MyISAM). Each INSERT/UPDATE/DELETE verb applies to one table only, and may affect other tables by REFERENCE.
For non-standard SQLs, you have the DELETE multiple_table method (non-standard syntax).
First, it is very important to understand this, before anything else. The FROM and WHERE (or JOIN) clauses in a DELETE command are separate to the DELETE itself; they are in fact a SELECT. The idea is: DELETE table_one (SELECT FROM table_one, table_two WHERE join_conditions).
Therefore:
bulletin
PK affecting the thre child tables)bulletin
composite Primary Key to the deleted.Here's a link to the DELETE syntax and the JOIN syntax.
Comments 12 Jan 11 21:48 and Response
a. Don't be lazy. Write four delete statements, bottom up. That's what we have to do in the big end of town, where we do not have "cascading deletes". Write the delete for rating_response; then copy-and-paste, and delete one line of code each time. I do not understand the angst or avoidance.
b. I repeat, do not use left, right, or any kind of outer join (which is only required for the single all-encompassing delete). Use straight inner joins only (which is not a problem with 4 delete statements). Any and all upset you are experiencing is due to your need to use one delete. Give that up, and the upset and complication disappear.
SELECT
command. Three-column PK times four Subqueries. I do not understand the need to avoid long (demanded, again, due to SQL being cumbersome) commands. And I didn't even use the JOIN syntax. Took me all of ten mins to write, plus five mins to test. What exactly, is the big deal ?c. You have not forgotten the power of Relational keys, that you recognised some weeks ago, have you. Eg. ability to grab bulletin from rating_response, without having to join with rating. If you succumb to your single-column-key desires, you will lose all that. SQL is cumbersome. But that is all we have. Deal with it. The non-SQLs try to "make life easy" but in fact, introduce all sorts of unnecessary and avoidable complications. Case in point.
Comments of 13 Jan 2011 21:18 PST and Response
Deletes. Three flavours. Great. Hopefully you will have the data values in $variables, so there will not be that form of repetition. For testing, that is fine.
Delete x Four Tables. (not "Indiviudally deleting records", which is a different thing altogether; each delete except the last could net hundreds of rows). I trust the cut-and-paste took seconds. You need to be careful about forgetting to change the table names.
Single Delete Command. $variables for the first triplet. You could use column names in all but the first triplet.
Ok, so you will convert the SELECT to a DELETE, after testing. Left Joins. Required for the Single Delete but not otherwise. That's identical to (2) with the WHERE replaced with JOIN>
I've already recommended (1), but you are more likely to go with (3).
不要害怕加入。如果我是你,我会减少你需要编写的所有数据库逻辑,并使用像 Doctrine 或 Propel 这样的 ORM,这将使设计和维护变得更加容易——包括你试图避免的所有连接。
Dont be afraid of joins. If i were you i would cut down on all the DB logic you need to write and use an ORM like Doctrine or Propel, it will make things infinitely easier to design and maintain - including all those joins youre trying to avoid.