How are URLs (fragments) stored in a relational database?
In the following URL fragment:
~/house/room/table
it lists all the information on a table, and perhaps some information about the table.
This fragment:
~/house
outputs: Street 13 and Room, Garage, Garden
~/house/room
outputs: My room and Chair, Table, Window
What does the Database schema looks like? What if I rename house to flat?
Possible solution
I was thinking that I could create a hash for the URL and store it along with parentID and information. If I rename some upper-level segment I would then need to update all the rows which contain the given segment.
Then I thought would store each segment along with information and its level:
SELECT FROM items WHERE key=house AND level=1 AND key=room AND level=2
How do I solve this problem if the URL can be arbitrarily deep?
check The Adjacency List Model and The Nested Set Model described in Joe Celko's Trees and Hierarchies in SQL for Smarties
you should find plenty information to this topic. one article is here
Update
The Nested Set Model is very good if you are looking for a task like 'Retrieving a Single Path'. What you have is 'Find the Immediate Subordinates of a Node'. Here the Adjacency List Model is better.
| id | p_id | name |
| 1 | null | root |
| 2 | 1 | nd1.1 |
| 3 | 2 | nd1.2 |
| 4 | 1 | nd2.1 |
SQL to get a row with name of a fragment and it's direct sub items.
SELECT
p.name,
GROUP_CONCAT(
c.name
SEPARATOR '/'
) AS subList
FROM _table p
INNER JOIN _table c
ON p.id = c.p_id
WHERE p.name = 'root'
P.S. prefer WHERE p.id = 1. Id is unique where as name can be ambiguous.
see MySQL GROUP CONCAT function for more syntax details.
Related
I have come across this problem and I've tried to solve it few days now.
Let's say I have following tables
properties
-----------------------------------------
| id | address | building_material |
-----------------------------------------
| 1 | Street 1 | 1 |
-----------------------------------------
| 2 | Street 2 | 2 |
-----------------------------------------
building_materials
-----------------------------
| id | building_material |
-----------------------------
| 1 | Wood |
-----------------------------
| 2 | Stone |
-----------------------------
Now. I would like to provide an API where you could send a request and ask for every property that has building material of wood. Like this:
myapi.com/properties?building_material=Wood
So I would like to query database like this (I want to return the string value of building_material not the numeric value):
SELECT p.id, p.address, bm.building_material
FROM properties as p
JOIN building_materials as bm ON (p.building_material = bm.id)
WHERE building_material = "Wood"
But this will give me an error
Column 'building_material' in where clause is ambiguous
Also if I want to get property with id of 1.
SELECT p.id, p.address, bm.building_material
FROM properties as p
JOIN building_materials as bm ON (p.building_material = bm.id)
WHERE id = 1
Column 'id' in where clause is ambiguous
I understand that the error means that I have same column name in two tables and I don't specify which id I want like p.id.
Problem is I don't know how many query parametes API user is going to send and I would like to avoid looping through them and changing id to p.id and building_material to bm.building_material. Also I don't want that user has to send request to the API like this
myapi.com/properties?bm.building_material=Wood
I've thought about changing the properties table building_material to fk_building_material and changing properties table id to property_id.
I just don't like the idea that on client side I would then have to refer property's building material as fk_building_material. Is this a valid method to solve this problem or what would be the correct way of designing these tables?
The query mentions two tables, so all the columns in both tables are "on the table" for use anywhere in the query.
In one table building_material is an "id" for linking to the other table; in the other table, it is a string. While this is possible, it is confusing to the reader. And to the parser. To resolve the confusion, you must qualify building_material with which one you want; that is done with a table alias (or table) in front (as you did in all other places).
There are two ids are all ambiguous. But this is the "convention" used by table designers. So, it is OK for an id in one table to be different than the id in the other table. (p.id refers to one thing in one table; bm.id refers to another in another table.)
SELECT p.id, p.address, bm.building_material
FROM properties as p
JOIN building_materials as bm ON (p.building_material = bm.id)
WHERE bm.building_material = "Wood" -- Note "bm."
For some reason, I am unable to export a table of subscribers from my phpList (ver. 3.0.6) admin pages. I've searched on the web, and several others have had this problem but no workarounds have been posted. As a workaround, I would like to query the mySQL database directly to retrieve a similar table of subscribers. But I need help with the SQL command. Note that I don't want to export or backup the mySQL database, I want to query it in the same way that the "export subscribers" button is supposed to do in the phpList admin pages.
In brief, I have two tables to query. The first table, user contains an ID and email for every subscriber. For example:
id | email
1 | e1#gmail.com
2 | e2#gmail.com
The second table, user_attribute contains a userid, attributeid, and value. Note in the example below that userid 1 has values for all three possible attributes, while userid's 2 and 3 are either missing one or more of the three attributeid's, or have blank values for some.
userid | attributeid | value
1 | 1 | 1
1 | 2 | 4
1 | 3 | 6
2 | 1 | 3
2 | 3 |
3 | 1 | 4
I would like to execute a SQL statement that would produce a row of output for each id/email that would look like this (using id 3 as an example):
id | email | attribute1 | attribute2 | attribute3
3 | e3#gmail.com | 4 | "" | "" |
Can someone suggest SQL query language that could accomplish this task?
A related query I would like to run is to find all id/email that do not have a value for attribute3. In the example above, this would be id's 2 and 3. Note that id 3 does not even have a blank value for attributeid3, it is simply missing.
Any help would be appreciated.
John
I know this is a very old post, but I just had to do the same thing. Here's the query I used. Note that you'll need to modify the query based on the custom attributes you have setup. You can see I had name, city and state as shown in the AS clauses below. You'll need to map those to the attribute id. Also, the state has a table of state names that I linked to. I excluded blacklisted (unsubscribed), more than 2 bounces and unconfirmed users.
SELECT
users.email,
(SELECT value
FROM `phplist_user_user_attribute` attrs
WHERE
attrs.userid = users.id and
attributeid=1
) AS name,
(SELECT value
FROM `phplist_user_user_attribute` attrs
WHERE
attrs.userid = users.id and
attributeid=3
) AS city,
(SELECT st.name
FROM `phplist_user_user_attribute` attrs
LEFT JOIN `phplist_listattr_state` st
ON attrs.value = st.id
WHERE
attrs.userid = users.id and
attributeid=4
) AS state
FROM
`phplist_user_user` users
WHERE
users.blacklisted=0 and
users.bouncecount<3 and
users.confirmed=1
;
I hope someone finds this helpful.
I have three tables for tagging. The first one is the question table that has a list of question with certain ID. The second one is the tag table that has a list of tag name with certain ID. And the third one is the question_tag table, a collection of question to a tag. A question that has multiple tag means multiple rows in question_tag, I thought of storing an serialized array into the question_tag table but it's in general not a good idea to store array inside of a SQL database.
Below is the schema. Arrow denoting foreign key.
-------------------
----------------- | question_tag |
| question | ------------------- ------------------
----------------- | question_tag_ID | | tag |
| question_ID | ---> | question_ID | ------------------
----------------- | tag_ID | <----- | tag_ID |
------------------- | tag_name |
------------------
I want to make a query that will output this table below.
----------------------------------------------------
| question_id | tag_name |
----------------------------------------------------
| 1 | algebra, calculus, differentiation |
| 2 | calculus |
| 3 | algebra, trigonometry |
----------------------------------------------------
How do I manage to do this query? I thought about SELECTING from question and JOINING a temporary table of SELECT tag.tag_name FROM tag WHERE question_tag.tag_ID = tag.tag_ID, but how do I output this RIGHT column (tag_name) like the table above.
I would really appreciate it if you can help me with this SQL query, I am guessing that I need to do a nesting SELECT query for the RIGHT (tag_name) column, then JOIN it to the question_table. But I am not sure how to the nesting of SELECT query.
This is what I have come up with:
SELECT * FROM question as Q LEFT JOIN (SELECT T.tag_name FROM tag as T WHERE T.tag_id IN (SELECT QT.tag_id FROM question_tag AS QT WHERE QT.question_ID = Q.id)) AS QT_T
You'll need to aggregate and concatenate. Please check out some of the related questions:
Does T-SQL have an aggregate function to concatenate strings?
Implode type function in SQL Server 2000?
Concatenate row values T-SQL
How to use GROUP BY to concatenate strings in MySQL?
Aggregate String Concatenation in Oracle 10g
Create a delimitted string from a query in DB2
How to concatenate strings of a string field in a PostgreSQL 'group by' query?
UPDATE
While I don't have a mysql DB to test this on, going off of one of the above links (with the knowledge this is mysql), I've designed the following query:
SELECT
qt.question_id,
GROUP_CONCAT(t.tag_name SEPARATOR ',')
FROM question_tag qt
LEFT JOIN tag t ON t.tag_id = qt.tag_id
GROUP BY qt.question_id;
Please try this out and let me know if it works.
I'm having difficulty with the current model for my MySQL Table, namely that I can't seem to properly query all the child nodes of a specific parent. As the title states, I'm using the adjacency model.
The problem is that most methods I have found online either query all leaf nodes, or select more than just what I'm attempting to grab.
The first tutorial I was following was on MikeHillyer.com, and his solution was to the effect of:
SELECT t1.name FROM
category AS t1 LEFT JOIN category as t2
ON t1.category_id = t2.parent
WHERE t2.category_id IS NULL;
The problem with this, is it queries all of the leaf nodes, and not just the ones related to the parents. He also suggested using the Nested Set Model, which I REALLY don't want to have to use due to it being a little more difficult to insert new nodes (I do realize it's significance though, I'd just rather not have to resort to it).
The next solution I found was on a shared slideshow on slide 53 (found from another answer here on StackOverflow). This solution is supposed to query a node's immediate children, only... The solution does not seem to be working for me.
Here's their solution:
SELECT * FROM Comments cl
LEFT JOIN Comments c2
ON(c2.parent_id = cl.comment_id);
Now, my table is a little different, and so I adjusted some of the code for it. A brief excerpt of my table is the following:
Table: category
id | parent | section | title | ...
----+--------+----------+----------+-----
1 | NULL | home | Home | ...
2 | NULL | software | Software | ...
3 | 2 | software | Desktop | ...
4 | 2 | software | Mobile | ...
5 | NULL | about | About | ...
6 | 5 | about | Legal | ...
... | ... | ... | ... | ...
When I modified the above query, I did the following:
SELECT * FROM category cat1
LEFT JOIN category cat2
ON(category.parent = cl.id);
This resulted in EVERYTHING being queried and tied in a table twice as long as the unaltered table (obviously not what I'm looking for)
I'm pretty certain I'm just doing something wrong with my query, and so I'm just hoping someone here can correct whatever my mistake is and point me in the right direction.
I know it's supposed to be easier to use a Nested Set Model, but I just dislike that option for the difficulty of adding new options.
Looks like you're very close. Your left join is guaranteeing that all records from the table will be returned.
See the below query.
SELECT c1.*, c2.id FROM category c1 INNER JOIN category c2 ON (c1.parent = c2.id);
Let's say I have the following scenario.
A database of LocalLibrary with two tables Books and Readers
| BookID| Title | Author |
-----------------------------
| 1 | "Title1" | "John" |
| 2 | "Title2" | "Adam" |
| 3 | "Title3" | "Adil" |
------------------------------
And the readers table looks like this.
| UserID| Name |
-----------------
| 1 | xy L
| 2 | yz |
| 3 | xz |
----------------
Now, lets say that user can create a list of books that they read (a bookshelf, that strictly contains books from above authors only i.e authors in our Db). So, what is the best way to represent that bookshelf in Database.
My initial thought was a comma separated list of BookIDin Readers table. But it clearly sounds awkward for a relational Db and I'll also have to split it every time I display the list of users' books. Also, when a user adds a new book to shelf, there is no way of checking if it already exists in their shelves except to split the comma-separated list and and compare the IDs of two. Deleting is also not easy.
So, in one line, the question is how does one appropriately models situations like these.
I have not done anything beyond simple SELECTs and INSERTs in MySQL. It would be much helpful if you could describe in simpler terms and provide links for further reading.
Please comment If u need some more explanation.
Absolutely forget the idea about a comma separated list of books to add to the Readers table. It will be unsearchable and very clumsy. You need a third table that join the Books table and the Readers table. Each record in this table represent a reader reading a book.
Table ReaderList
--------------------
UserID | BookID |
--------------------
You get a list of books read by a particular user with
select l.UserID, r.Name, l.BookID, b.Title, b.Author
from ReaderList l left join Books b on l.BookID = b.BookID
left join Readers r on l.UserID = r.UserID
where l.UserID = 1
As you can see this pattern requires the use of the keyword JOIN that bring togheter data from two or more table. You can read more about JOIN in this article
If you want, you could enhance this model adding another field to the ReaderList like the ReadingDate