Joining MySQL results from the same table - mysql

I'm proficient with joining tables in mySQL, but I'm having trouble with joining results from the SAME table. I'm creating a folder structure in PHP where a folder has an ID, a parent ID, a random-string ID, and a label.
My DB looks like:
| id | parent_id | uniq | label
---------------------------------
| 1 | 0 | w2d4f6 | dir 1
| 2 | 1 | h9k3h7 | dir 2
The front end uses the uniq var to identify a folder. So In the DB you can see that if I am opening the folder dir 1, the folder dir 2 will be inside it, since dir 2 has dir 1's ID as its parent.
Still with me?
|- dir 1
| + dir 2
The folder dir 1 is identified by its uniq string, so w2d4f6. So what I'm wanting to do is:
Get the parent ID of the record that has uniq='w2d4f6'
The parent ID is 1
Look for records where parent_id=1
I know this is totally wrong, and I think I should be using JOIN but I tried the following without success.
SELECT folders.label,folders.parent_id FROM folders WHERE folders.uniq='w2d4f6' AND folders.id=folders.parent_id

To get the children of a folder:
select b.label, b.parent_id
from folders a, folders b
where a.uniq = 'w2d4f6' AND b.parent_id = a.id

This should work if you already have the parent data and just want to request the child data by the uniq value of the parent:
SELECT label, parent_id FROM folders WHERE parent_id IN (SELECT id FROM folders WHERE uniq='w2d4f6')

Related

SQL Query To Select From Either Or 3 Tables

Am building a document manager, where a file can belong to many folders(When copied action is being performed from front-end) and a folder can belong to many file likewise, also to keep in mind a file and folder can belong to another folder.With that design i opted for a many to many relationship as that makes a lot of sense. I have three tables folders,files,file_folder where the file_folder is the pivot table.
My Simple DB Schema is defined below :
files:
file_id | name
1 | document.docx
folders:
folder_id | name
1 | root
2 | images
file_folder:
file_id | folder_id | phash
NULL | 2 | 1
1 | NULL | 1
Now the problem is, i have been trying to write a join query that returns a list of both files and folders if they share the same folder,below are the list of queries i have tried but none has been able to return the desired result.
I pretty much want my result this way:
file_id | folder_id | name
1 | NULL | document.docx
NULL | 2 | images
SELECT * FROM
folders dir
JOIN file_folder dir_file
ON dir_file.dir_id = dir.dir_id
JOIN files file
ON dir_file.file_id = file.file_id
WHERE dir_file.dir_id = 1 ORDER BY dir_file.created_at
SELECT * FROM
folders dir,files file
JOIN file_folder dir_file
ON (dir_file.file_id = file.file_id OR dir_file.dir_id = dir.dir_id)
WHERE dir_file.dir_id = 1 ORDER BY dir_file.created_at
I know am just missing something out, that i can't figure out yet. Or maybe am just getting the DB Schema totally wrong. I really don't want to put files and folder on the same table and reference phash(parent folder), this will work but copying a folder with files into another one would just only copy the folder and not the files inside of it, as i will have to duplicate all the sub folders and files to make that happen, which is very bad. I will appreciate if i can get a solution to this
EDITED:
Below works fine for me now!
SELECT * FROM folders dir
LEFT JOIN file_folder dir_file
ON dir_file.dir_id = dir.dir_id
LEFT JOIN files file
ON dir_file.file_id = file.file_id
WHERE dir_file.phash = 1 ORDER BY dir_file.created_at
Due the fact you have not always match between the tables you should use left join
SELECT *
FROM folders dir
LEFT JOIN file_folder dir_file ON dir_file.dir_id = dir.dir_id and dir_file.phash = 1
LEFT JOIN files file ON dir_file.file_id = file.file_id
ORDER BY dir_file.created_at
And in left join move the where condition in On clause (otherwise work as a inner join )

Adding entries from multiple MySQL tables using one single SQL join statement, but only if there are entries available in the second table

Using one single SQL query with a join:
How can I add entries from a second table only if there is a corresponding entry available?
project source
description | source source_id | value
---------------------------- --------------------------------
Project 1 | 1 1 | Additional Info 1
Project 2 | null
When I type
select project.description, source.value
from project, source
where project.source = source.source_id
and project.description = "Project 1";
As desired I receive
Project 1 | Additional Info 1
However when I replace Project 1 with Project 2 in the last line, I won't get a result, because project.source is null.
Is it possible to use a single SQL query which outputs something like this?
Project 2 | null
I´m looking for a query which covers both cases.
Any ideas?
You can use a LEFT JOIN on the project table to make sure that all projects appear in the result set even if they have no matching value in the source table. Projects from the project table which do not match will have NULL for their value.
SELECT project.description AS description, source.value AS value
FROM project LEFT JOIN source
ON project.source = source.source_id
Output:
+--------------+--------------------+
| description | value |
---------------+--------------------+
| Project 1 | Additional Info 1 |
| Project 2 | null |
+--------------+--------------------+
Try to use left join....
SELECT project.description, source.value FROM project LEFT JOIN source ON project.source = source.source_id;

export phpList subscribers via sql in mysql database

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.

mysql count votes optimization

so im making a file hub nothing huge or fancy just to store some files that may be shared by others for download. and it just occured to me in the way that i originally intended to count the amount of upvotes or downvotes the query could be server heavy.the query to get the files is something along the lines of
select*from files;
and in such i would recieve an array of my files that i could loop over and get specifics on each file now with the inclusion of voting a file that same foreach loop would include a further query that would get the count the amount votes a file would get (the file id in the where clause) like so
select*from votes where upvoted=true and file.id=?
and i was thinking of using pdo::rowCount to get my answer. now evey bone in my body just says this is bad very bad as imagine im getting 10,000 files i just ran 10,000 extra queries one on each file and i havent looked at the downvotes yet which i was think could go in a similar fasion. any optimization adviece here is a small rep of the structure of a few tables. the upvoted and downvoted columbs are of type bool or tinyint if you will
table: file table: user table: votes
+----+-------------+ +----+-------------+ +--------+--------+--------+--------+
| id |storedname | | id | username | |file_id | user_id| upvoted | downvoted
+----+-------------+ +----+-------------+ +--------+--------+--------+--------+
| 1 | 45tfvb.txt | | 1 | matthew | | 1 | 2 | 1 | 0
| 2 |jj7fnfddf.pdf| | 2 | mark | | 2 | 1 | 1 | 1
| .. | .. | | .. | .. | | .. | .. | .. | ..
there are two ways to do this. the better way to do this (aka faster) is to write separate queries and build into one variable in your programming language (like php, python.. etc.)
SELECT
d.id as doc_id,
COUNT(v.document_id) as num_upvotes
FROM votes v
JOIN document d on d.id = v.document_id
WHERE v.upvoted IS TRUE
GROUP BY doc_id
);
that will return your list of upvoted documents. you can do the same for your downvotes.
then after your select from document do a for loop to compare the votes with the document by ID and build into a dictionary or list.
The second way to do this which can take a lot longer at runtime if you have a bunch of records in the table (its less efficient, but easier to write) is to add subquery selects in your select statement like this...
SELECT
logical_name ,
document.id ,
file_type ,
physical_name ,
uploader_notes ,
views ,
downloads ,
user.name ,
category.name AS category_name,
(Select count(1) from votes where upvoted=true and document_id=document.id )as upvoted,
(select count(1) from votes where upvoted=false and document_id=document.id) as downvoted
FROM document
INNER JOIN category ON document.category_id = category.id
INNER JOIN user ON document.uploader_id = user.id
ORDER BY category.id
Two advices:
Avoid SELECT * especially if you're going to count. Replace it, with something like that:
SELECT COUNT(1) AS total WHERE upvoted=true AND file.id=?
Maybe you want to create a TRIGGER to keep update a counter in the file table.
I hope it will be helpfull to you.

How do I store URL fragments in a database?

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.