"Secondary" query - mysql

I'm new to MySQL and I'm having trouble trying to figure out how to solve the following problem:
1 - I have a table with data about some people, including a name, a number code and a category for each one.
Let's supose "JOHN DOE" is one of them, his number code is "1111" and his category is "A"; "FOO" is another one, with number code "2222" and category "B"; and "BAR" is another one, number code "3333" and category "A".
2 - Everyone in category "A" was suposed to fill out a form which filled a second table in my database. In this table, the reference to the responsible for each answer is his/her number code. Each person could also answer the form more than one time.
The problem is: Now I need a table that shows me who answered the form and who did't.
PS: Let's call the database "data_base", the first table "people" (with columns "name", "num_code" and "category") and the second table "answers" (with columns "num_code" and "answer").
Could someone help me?

This can be simply solved with a subquery:
SELECT
`num_code`,
`name`,
(
SELECT COUNT(*)
FROM `answers`
WHERE `num_code` = `people`.`num_code`
) AS `AnswerCount`
FROM
`people`
ORDER BY
`name`
There are a number of ways to solve this problem in SQL. One of the advantages of the above result format is that you will get the number of answers per person. Therefore if it is 0, then you know they didn't fill one out.

You can do this with a so called 'LEFT JOIN'. It gets all records in people and links them to the records in answers. Then using the where clause, all records that indeed have an answer are removed from the result and only those who don't are returned.
select
p.*
from
people p
left join answers a on a.num_code = p.num_code
where
a.num_code is null
Another solution is to use NOT EXISTS. It is actually a bit cleaner to use that, but also a little more complex (you will need a sub-select). And it has proven (to me) to be slower in MySQL.

Related

MySQL Query <table_name> based on column value return

This may be a ridiculous question with a far more straightforward solution than I am leaning towards here, but I'm hoping someone can give me a good solution!
Let's say I have a database with multiple tables that look something like this:
**table_users**:
id, username, email, password
*example*
testuser, testuser#test.com, 123456
**table_services**:
id, service_name
*example*
0, Shipping
1, Payments
2, Packaging
**table_markets:**
id, market_name
*example*
0, Fashion
1, Food
2, Healthcare
**table_user_attributes:**
user_id, table_name, attr_id
*example*
0, 'table_markets', 1
0, 'table_services', 0
0, 'table_services', 2
In this example, the user attributes table would have user id of 0 associated with 'Food' market and the 'Shipping' and 'Packaging' services.
The reason I have the table name in this attributes column in this example, is these attribute ID's can come from different tables (services, markets in this case) and I need to kow which table to look up the attr_id for.
My FIRST question is - im guessing im doing the table name setup in a very dumb way, is there a better way to locate what table something might be coming from, and then be able to use that in your next query?
my second question is, assuming this is correct, how would I then set up a query to do something like:
look up user, get user id, look up all entries from table_user_attributes with the user id I have received, and then (here's the question part) take the table name and query those with the ids... i.e.:
SELECT table_users.username, table_users.id, table_user_attributes.table_name,
table_user_attributes.attr_id
FROM table_user_attributes
INNER JOIN table_users ON table_user_attributes.user_id=table_users.id
If I'm understanding right, that query would get me the users info, as well as the table_user_attributes associated with that user id, but then I have no idea how I then pull the appropriate services/markets from this info.
I hope that makes sense, it's a strange question - basically im aiming to keep user attributes all in one table, but the ID's that they point to can come from multiple different tables, and I need a way to know from which table each ID associated with the USER id comes from so i can look it up and get its name.
The problems likely enter in:
Database design - I'm quite sure this is not perfect (also this doesn't exist, this is me writing up a theoretical example, I haven't built any tables yet, set primary keys, etc. trying to wrap my head around this issue first.
Query - which will change depending on the design - but if, on the off chance I used what I have above, I don't know how id make that query work.
Probably going about it wrong - so any help is much appreciated!
EDIT
I also realize the easiest (and possibly best) solution is to have a separate table for each thing, like:
table_user_services
table_user_markets
etc - but I guess I just want to check if that is the most efficient solution or if this is a more btree/sql friendly solution.

SQL Left/Inner/Normal Join vs Where while conditional statement

I am relatively new to the SQL programming, so please go easy on me.
I am currently writing a query, which would output the result based on the value from one of the outer parameters. The structure is currently looking like following:
#ShowEntireCategory bit = 0
select distinct
p.pk
p.name
--other columns
from dbo.Project P
--bunch of left joins
where p.Status = 'Open'
--other conditions
What I am trying to implement is: when the value of ShowEntireCategory is 1 (changed programmatically through radiobutton selection) it will show records of all subcategories, which are inside of the the category. When it is 0, it will only show records from the selected subcategory, while other subcategories in that category remains untouched.
I have been performing a research on the best approach, and it narrowed down to either WHERE statements or JOINs.
What I want to know is: which of these approaches I should use for my scenario? In my case the priority is optimization (minimum time to execute) and ease of implementation.
NOTE: My main goal here is not to receive a ready to use code here (though an example code snippets would be welcome), I just want to know a better approach, so I can continue researching in that direction.
Thank you in advance!
UPDATE
I have performed additional research on the database structure, and managed to narrow down to parameters relevant to the question
One is dbo.Project table, which contains: PK, CategoryKey (FK) (connected to the one in second table), Name, Description, and all other parameters which are irrelevant.
Second one is dbo.Area table, which contains: PK, AreaNumber, Name, CategoryKey (FK), IsCategory (1 = is category, 0 = not category).
Sorry, but I work in fast-paced environment, this is as much as I was able to squeeze. Please let me know if it is not enough.
With the information you provided the best solution would be to use a combination of WHERE clauses and JOINS. You would likely need to use a WHERE clause on the second table (described in the update) to select all rows which are categories. Then, you would JOIN this result with your other tables/data. Finally, you can use a CASE clause (details found here) to check your variable and determine if you need all categories or just some (which can be dealt with through an additional WHERE clause).
Not sure this entirely answers your question, but for a more detailed answer we would need a more detailed description of the database schema.

How to select number with LIKE?

I have a bunch of products, and a bunch of category pages. One product can be in multiple categories. So in my database I have a products table with a "categories" column. In this column I store the ID's of all the categories that the current product is stored in, its a string seperated with semicolons.
Example: 1;5;23;35;49;.
When I browse to Category Page ID 5, I want to see all products that have 5; in its categories-column. I currently do this by
SELECT * FROM products WHERE categories LIKE "%".category.";%"
The problem is that this matches more than just 5. It matches 15; or 25; aswell.
So questions:
How do I make sure that I only select the number I want? If category is "5" I do not want it to match 15, 25, 35 and so on.
Maybe this is a very bad way of storing the category-ids. Do you have any suggestions of a different way of storing what products that belong to what category?
Others have mentioned that a junction table is the right way to design the database. SQL has a very nice data structure for storing lists. It is not called a "string", it is called a "table".
But, sometimes one is stuck with data in this format and needs to work with it. In that case, the key is to put the delimiters on both side to prevent the problem you are having:
SELECT *
FROM products
WHERE concat(';', categories) LIKE "%;".category.";%"
Your list already ends in a semicolon, so that is not necessary.
Another more typical MySQL solution is find_in_set():
SELECT *
FROM products
WHERE find_in_set(category, replace(categories, ';', ',') > 0;
It is designed for comma-delimited lists. Odd that MySQL supports such a function when storing lists this way is generally a bad idea, but it does. Still, a junction table is better for performance reasons (and for other reasons).
Answers/comments to your two questions:
The only way I can think of that you could do this without modifying your schema (see #2) is to use a MySQL regular expression but this is really not a good idea. See http://dev.mysql.com/doc/refman/5.1/en/regexp.html for documentation though
You are right - this is not a good way to store categories. What you want is a join also known as a junction table (see http://en.wikipedia.org/wiki/Junction_table). One way would be to have three tables: product, category, and a product_categories table. Product and category would have a unique ID as you already have and the product_categories table would have two columns: product_id and category_id. If product 1 belongs to categories 10 and 11, you would have two rows in the product_categories table: 1,10 and 1,11.
I can elaborate if you need more help but this should get you started in re-architecting your database (more) correctly.
You can try changing your like criteria to "%;".category.";%"

Store Voting Information - Database Outline

Summary: What is the most efficient way to store information similar to the like system on FB. Aka, a tally of likes is kept, the person who like it is kept etc.
It needs to be associated with a user id so as to know who liked it. The issue is, do you have a column that has a comma delimited list of the id of things that were liked, or do you have a separate column for each like (way too many columns). The info that's stored would be a boolean value (1/0) but needs to be associated with the user as well as the "page" that was liked.
My thought was this:
Column name = likes eg.:
1,2,3,4,5
Aka, the user has "like" the pages that have an id of 1, 2, 3, 4 and 5. To calculate total "likes" a tally would need to be taken and then stored in a database associated with the pages themselves (table already exists).
That seems the best way to me but is there a better option that anyone can think of?
P.S. I'm not doing FB likes but it's the easiest explanation.
EDIT: Similar idea to the plus/neg here on stackoverflow.
In this case the best way would be to create a new table to keep track of the likes. So supposing you have table posts, which has a column post_id which contains all the posts (on which the users can vote). And you have another table users with a column user_id, which contains all the users.
You should create a table likes which has at least two columns, something like like_postid and like_userid. Now, everytime a user likes a post create a new row in this table with the id of the post (the value of post_id from posts) that is liked and the id of the user (the value of user_id from users) that likes the post. Of course you can enter some more columns in the likes table (for instance to keep track of when a like is created).
What you have here is called a many-to-many relationship. Google it to get some more information about it and to find some more advice on how to implement them correctly (you will find that a comma seperated lists of id's will not be one of the best practices).
Update based on comments:
If I'm correct; you want to get a list of all users (ordered by name) who have voted on an artist. You should do that something like:
SELECT Artists.Name, User.Name
FROM Artists
JOIN Votes
ON Votes.page_ID = Artists.ID
JOIN Users
ON Votes.Votes_Userid = Users.User_ID
WHERE Artists.Name = "dfgdfg"
ORDER BY Users.Users_Name
There a strange thing here; the column in your Votes table which contains the artist id seems to be called page_ID. Also you're a bit inconsistent in column names (not really bad, but something to keep in mind if you want to be able to understand your code after leaving it alone for 6 months). In your comment you say that you only make one join, but you actually do two joins. If you specify two table names (like you do: JOIN Users, Votes SQL actually joins these two tables.
Based on the query you posted in the comments I can tell you haven't got much experience using joins. I suggest you read up on how to use them, it will really improve your ability to write good code.

Table join--multiple rows to/ from one column (/ cell)

I have searched for a solution for this problem, but haven't found it (yet), probably because I don't quite know how to explain it properly myself. If it is posted somewhere already, please let me know.
What I have is three databases that are related to each other; main, pieces & groups. Basically, the main database contains the most elementary/ most used information from a post and the pieces database contains data that is associated with that post. The groups database contains all of the (long) names of the groups a post in the main database can be 'posted in'. A post can be posted in multiple groups simultaneously. When a new post is added to my site, I check the pieces too see if there are any duplicates (check if the post has been posted already). In order to make the search for duplicates more effective, I only check the pieces that are posted in the same group(s).
Hopefully you're still with me, cause here's where it starts to get really confusing I think (let me know if I need to specify things more clearly): right now, both the main and the pieces database contain the full name of the group(s) (basically I'm not using the groups database at all). What I want to do is replace the names of those groups with their associated IDs from the groups database. For example, I want to change this:
from:
MAIN_table:
id  |  group_posted_in
--------|---------------------------
1   | group_1, group_5
2   | group_15, group_75
3   | group_1, group_215
GROUPS_table:
id  |  group_name
--------|---------------------------
1   | group_1
2   | group_2
3   | group_3
etc...
into:
MAIN_table:
id  |  group_posted_in
--------|---------------------------
1   | 1,5
2   | 15,75
3   | 1,215
Or something similar to this. However, This format specifically causes issues as the following query will return all of the rows (from the example), instead of just the one I need:
SELECT * FROM main_table WHERE group = '5'
I either have to change the query to something like this:
...WHERE group = '5' OR group = '5,%' OR group = '%,5,%' OR group = '%,5'
Or I have to change the database structure from Comma Separated Values to something like this: [15][75]. The accompanying query would be simpler, but it somehow seems like a cumbersome solution to me. Additionally, (simple) joins will not be easy/ possible at all. It will always require me to run a separate query to fetch the names of the groups--whether a user searches for posts in a specific group (in which case, I first have to run a query to fetch the id's, then to search for the associated posts), or whether it is to display them (first the posts, then another query to match the groups).
So, in conclusion: I suppose I know there is a solution to this problem, but my gut tells me that it is not the right/ best way to do it. So, I suppose the question that ties this post together is:
What is the correct method to connect the group database to the others?
For a many-to-many relationship, you need to create a joining table. Rather than storing a list of groups in a single column, you should split that column out into multiple rows in a separate table. This will allow you to perform set based functions on them and will significantly speed up the database, as well as making it more robust and error proof.
Main
MainID ...
Group
GroupID GroupName
GroupsInMain
GroupsInMainID MainID(FK) GroupID(FK)
So, for MainID 1, you would have GroupsInMain records:
1,1,1
2,1,5
This associates groups 1 and 5 with MainID 1
FK in this case means a Foreign Key (i.e. a reference to a primary key in another table). You'd probably also want to add a unique constraint to GroupsInMain on MainID and GroupID, since you'd never want the same values for the pairing to show up more than once.
Your query would then be:
select GroupsInMain.MainID, Group.GroupName
from Group, GroupsInMain
where Group.GroupID=GroupsInMain.GroupID
and Group.GroupID=5