I have a problem at the moment where I'm trying to get all rows plus a selected row from my tables.
So I have three tables - users, tasks, and a joining table called tasks_users
The latter table just contains two columns - task_id and user_id, for the purpose of normalization.
I have a form to edit a task. The problem arises when I want to retrieve ALL users (so the task can be reassigned) and I also want to show the CURRENT user. Given my current queries I can only get all the users, or just the single related user.
Am I overlooking something obvious, or do I need a more complex query? Or perhaps a couple of queries?
Further clarification:
Imagine you have an "edit task" form and on it you have the user currently assigned. I want to also show all other users, so that I may reassign. Given that I have three tables I'm struggling to construct a query that returns what I want. Currently I can only return ALL user OR the single assigned user.
It seems it's not about the current user, but the task assignee. No matter who assigns the task, he should not be able to assign it to the current assignee.
If only one user can be assigned to a particular task, you can remove task_users table, because simply adding column user_id in table tasks will do the same job.
Let's say you have in table users
id | username |
---------------------
1 | A |
2 | B |
3 | C |
4 | X |
5 | Y |
And in table tasks
id | title | user_id |
-----------------------------------
1 | Bla | 3 |
2 | Asd | NULL | // if this is even possible
If you want to retrieve who you can assign to task_id = 1 (Bla), you can simply do:
$currentTaskID = 1; // or however you retrieve them, it's just for the explanation purpose
SELECT id, username FROM users WHERE id != (SELECT user_id FROM tasks WHERE id = $currentTaskID);
The output will be:
id | username |
---------------------
1 | A |
2 | B |
4 | X |
5 | Y |
If a task can be assigned to more than one user, and you will keep task_users table, we can imagine you have there:
task_id | user_id |
-------------------------
1 | 3 |
2 | NULL |
1 | 5 |
You can query this way:
$currentTaskID = 1; // or however you retrieve them, it's just for the explanation purpose
SELECT id, username FROM users WHERE id NOT IN (SELECT user_id FROM task_users WHERE task_id = $currentTaskID);
So the output will be:
id | username |
---------------------
1 | A |
2 | B |
4 | X |
If you permit NULL values in assignee, so a task is not assigned to anyone, the SELECT will return all users, because of WHERE id != NULL when you have no NULL id's in users
For the purpose of showing the current user somewhere, but not available for assigning, you should separate the queries in functions/methods.
Saying you have:
function getAvailableAssignees($task_id) {
$sql = "SELECT id, username FROM users WHERE id != (SELECT user_id FROM tasks WHERE id = $task_id);";
$this->query($sql);
return $this->result();
}
and
function getCurrentAssignee($task_id) {
$sql = "SELECT user_id FROM tasks where id = $task_id;";
$this->query($sql);
return $this->result();
}
So once you need to show the available assignees, you call the respective method, so do, when you need to show the current assignee.
I think you might be looking for a UNION ALL
SELECT username, status from table
UNION ALL
SELECT username, status from table where status = 'current' LIMIT 1
Make sure that amount of columns from first query matches the amount of columns from second query. Don't do SELECT * FROM in one query and SELECT username from in another.
I think you should do 2 queries, not 1.. It doesn't seem like the users table should be in the same data selection as the tasks and users selection.
If you insist on combining the results you can do use UNION DISTINCT to combine the results of 2 queries.
THe first query decides the columns to be returned so you should set it to be the joined tables since those are more columns. The users should also fit in the columns of the joined select since it already contains them.
Thank you for all the replies. I have however managed to fix the issue using a nested foreach loop:
<ul>
<?php foreach($stages as $stage):?>
<li>
<label>
<?php echo $stage['stage_id']; ?>
<input type="checkbox" name="stages[]" value="<?php echo $stage['stage_id']; ?>"
<?php
// loop through the assigned stages. if it matches the stage ID then echo checked
foreach ($assigned_stages as $assigned_stage)
{
if ($stage['stage_id'] === $assigned_stage['stage_id'])
{
echo 'checked';
}
}
?>
/>
<?php echo $stage['name']; ?>
</label>
</li>
<?php endforeach; ?>
</ul>
This is a very common case and I'm still unsure if this is the best approach, however it works without issue and there isn't a great deal of code involved, so for the time being I'm happy.
Related
I have Two tables in ManyToMany relation:
Table Molécules:
id | main_name | others …
--- | --------- | ------
1 | caféine | others …
Table jsonTextMining:
id | title | molecule_name | others …
---|------- |-------------------------------------|------
1 | title1 | colchicine, cellulose, acid, caféine| others …
and 1 intermediate table:
Table json_text_mining_molecule (it's an exemple i don't succeed to fill it):
json_text_mining_id | molecule_id
------------------------ | ---------------
1 | corresponding molecule id's
1 | corresponding molecule id's
2 | corresponding molecule id's
My problem is that molecule_name in jsonTextMining are a string, i need to separate them before anything.
I tried this :
$molecules = explode (', ', $jsonTextMining→getMoleculeName());
foreach ($molecules as $molecule) {
$jsonTextMining->setMolecule($molecule);
}
$em->persist($jsonTextMining);
$em->flush;
But i think i should loop on jsonTexMining too and to be honnest i'm not sure where to put this part of code. Is it on a random page and the code will execute, should i do a button ?
I know exactly how to fill table with id's when there is a OneToMany relation, i use sql like this :
UPDATE table1 SET id_relation1 = table2.id
FROM table2
WHERE table1.main_name = table2.main_name
But this code fill only one column with id and there's always the matter of string. Is there really a way to get these id's linked so every molecule will have several jsonTextMining ?
You can first split the string using regexp_split function:
select id, regexp_split_to_table(molecule_name,', ') as m_name from jsonTextMining
That will give you a table of ids and names:
id | name
----+------------
1 | acid
1 | caffeine
1 | cellulose
1 | colchicine
Next, you can read from the above, match the names to the ids in the molecule table and aggregate the ids. All put together would result in this:
select s.id, string_agg(m.id::text, ', ')
from (select id, regexp_split_to_table(molecule_name,', ') as m_name
from jsonTextMining) as s, molecules m
where m.main_name = s.m_name group by s.id;
Which gives this result:
id | string_agg
----+------------
1 | 4, 1, 3, 2
(1 row)
If you don't want to aggregate the results and display them one row per molecule then just get rid of string_agg and the group by:
select s.id, m.id
from (select id, regexp_split_to_table(molecule_name,', ') as m_name
from jsonTextMining) as s, molecules m
where m.main_name = s.m_name;
I have these two tables which I'm trying to make an output of.
The first one **USERS** stores all information about a user, including an unique ID (androidID).
The second one gets input based on number of laps a user has taken, and will make one row pr lap.
What I'm trying to do is to output the last entry of a given androidID in **ROUNDS**, whith the corresponding name etc. from **USERS**
_____________________ _________________________
| **USERS** | | **ROUNDS** |
--------------------- -------------------------
| NAME | | ID(unique) |
| LASTNAME | | TIME |
| androidID(unique) | <----> | androidID(NOT unique) |
| ... | | ROUNDS |
This is how I'm quering the server
$result_users = $con->query(
"SELECT * FROM users"
);
$result_rounds = $con->query(
"SELECT * FROM Rounds ORDER BY laps desc"
);
I tried to numerous combination of the following. With no luck. My PHP skills is not the best, I'm afraid.
foreach ($result_users as $row_users) {
foreach ($result_rounds as $row_rounds) {
if($row_users['androidID'] == $row_rounds['androidID'] {
// Do some wizzardy
}
}
}
I have really hit a wall trying to connect the tables.
This would be the sql statement you want in your query.
SELECT * FROM `**USERS**` LEFT JOIN `**ROUNDS**`ON `**USERS**`.`androidID` = `**rounds**`.`androidID` ORDER BY `laps` desc 0,1;
Here’s what I have :
I have table 1 which is the parties table. It lists all the parties that are scheduled for the month.
I have 4 other tables, each contains the type of party favours that we use.
party_toys
party_drinks
party_foods
party_balloons
I need to query all parties that start this coming Saturday and list them back until the beginning of the year.
I have accomplished this.
What I need to do is figure out how to search but the party name from parties table and search all the party favours tables and list the results.
Still sorted this Saturday to the beginning of the year and filter out parties which we do not have any favours.
Currently I can search only by party name from the parties table. I can’t figure out how to search all of them at the same time.
I also need to paginate all of these.
I currently have pagination working but it currently lists everything including the parties that have no favours assigned.
I am stuck. Does this make sense?
Structure
For the parties table:
| party_id | parent_id | party_title | party_description | party_start | party finish |
For the party_drinks table:
| party_id | drink_id | drink_title | drink_notes |
For the party_foods table:
| party_id | food_id | food_title | food_notes |
For the party_toys table:
| party_id | toy_id | toy_title | toy_notes |
For the party_balloons table:
| party_id | balloon_id | ballon_title | balloon_notes |
SELECT parties.*, party_toys.*, party_drinks.*, party_foods.*, party_balloons.*
FROM parties
INNER JOIN party_toys ON parties.id = party_toy.party_id
INNER JOIN party_drinks ON parties.id = party_drinks.party_id
INNER JOIN party_foods ON parties.id = party_foods.party_id
INNER JOIN party_balloons ON parties.id = party_balloons.party_id
WHERE parties.title LIKE "%Super party%"
OR party_toys.toy_title LIKE "%Super toy 9000%"
OR party_drinks.drink_title LIKE "%Tequila%"
OR party_foods.food_title LIKE "%Burger%"
This is going to select all the data from all the table so feel free to customize the infos you want to select. Then it's going to link all of them, and depending on the parameters you set on the WHERE it's going to return you the stuff you ask for.
Also for pagination you can do it using a get parameter from your url :
your_site.com/search.php?page=12
Get the page number before your query :
if( isset($_GET['page']) and is_int($_GET['page']) )
$page = $_GET['page'];
else
$page = 0;
$results_per_page = 20;
$offset = $page * $results_per_page;
Once you have your page number you just add this parameter at the end of your query
$query = "all the query i wrote above";
$auer .= " LIMIT ".$offset.", ".$results_per_page;
In my table 'users' there are 'friends' ,
Like this :
+----+------+---------+
| id | name | friends |
+----+------+---------+
| 1 | a | 0,1,2 |
| 2 | b | 0,1,3 |
| 3 | c | 0,1 |
+----+------+---------+
How do I use the explode function to get the friends id one by one (not 0,1,2) that are separated by a comma (,) ;
How do I select the id? (Example) :
$sql = Select id from users where id = (exploded)
if (mysql_num_rows($sql) > 0 ) {
$TPL->addbutton('Unfriend');
}else{
$TPL->addbutton('Add as Friend')
}
The solution here is actually a slight change in your database structure. I recommend you create a "many-to-many" relational table containing all of the users friends referenced by user.
+---------+-----------+
| user_id | firend_id |
+---------+-----------+
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 2 | 1 |
| 2 | 5 |
+---------+-----------+
If you are storing lists of values within one field then that is the first sign that your database design is not quite optimal. If you need to search for a numerical value, it'll always be better to place an index on that field to increase efficiency and make the database work for you and not the other way around :)
Then to find out if a user is a friend of someone, you'll query this table -
SELECT * FROM users_friends WHERE
`user_id` = CURRENT_USER AND `friend_id` = OTHER_USER
To get all the friends of a certain user you would do this -
SELECT * FROM users_friends WHERE `user_id` = CURRENT_USER
Just a simple example that will make you clear how to proceed:
// Obtain an array of single values from data like "1,2,3"...
$friends = explode(',', $row['friends']);
Then, back in your query:
// Obtain back data like "1,2,3" from an array of single values...
$frieldslist = implode(',', $friends);
$sql = "SELECT * FROM users WHERE id IN ('" . $frieldslist . "')";
to get an array of if ids from your string explode would be used like this
$my_array = explode("," , $friends);
but you'd probably be better using the mysql IN clause
$sql = "Select id from users where id in (".$row['friends'].")";
Just a quick idea. Change your database's table. It is certain that after a while many problems will arise.
You could have something like this.
id hasfriend
1 2
1 3
2 1 no need to be here (You have this already)
2 4
.....
You can do this by using indexes for uniqueness or programming. You may think of something better. Change your approach to the problem to something like this.
I have two tables. The first table holds simple user data and has the columns
$username, $text, $image
(this is called "USERDATA").
The second table holds information about which users "follow" other users, which is set up with the columns
$username and $usertheyfollow
(this is called "FOLLOWS").
What I need to do is display the data individually to each user so that it is relevant to them. This means that userABC for instance, needs to be able to view the $text and $image inputs for all of the users whom he/she follows. To do this, I believe I need to write a sql query that involves first checking who the logged in user is (in this case userABC), then selecting all instances of $usertheyfollow on table FOLLOWS that has the corresponding value of "userABC." I then need to go back to my USERDATA table and select $text and $image that has a corresponding value of $usertheyfollow. Then I can just display this using echo command or the like...
How would I write this SQL query? And am I even going about the database architecture the right way?
With tables like so:
userdata table
______________________________
| id | username | text | image |
|------------------------------|
| 1 | jam | text | image |
+------------------------------+
| 2 | sarah | text | image |
+------------------------------+
| 3 | tom | text | image |
+------------------------------+
follows table
_____________________
| userId | userFollow |
|---------------------|
| 1 | 2 |
+---------------------+
| 1 | 3 |
+---------------------+
and use the following SQL:
SELECT userdata.text, userdata.image FROM follows LEFT JOIN userdata ON follows.userFollow = userdata.id WHERE follows.userId = 1
will get all the text and images that user with id '1' follows
As it turns out, neither of these answers were right. #jam6459 was closest.
The correct answer is the following:
SELECT userdata.text, userdata.image, follows.userFollow
FROM userdata
LEFT JOIN follows ON follows.userFollow = userdata.username
WHERE follows.userId = $username
I also found it easier to not have a username correspond to an Id as in jam's table example. This is because the same user can have multiple entries in "USERDATA". I instead used username as the Id.
function get_text_image($username)
{
$sql = "SELECT * FROM USERDATA where username='".$username."'";
$result = mysql_query($sql);
while($row = mysql_fetch_array($result))
{
echo $row['text'];
echo $row['image'];
}
}
function display_data_of_followers($userid)
{
$sql = "SELECT usertheyfollow FROM follow WHERE userid = ".$userid."";
$result = mysql_query($sql);
while($row = mysql_fetch_array($result))
{
get_text_image($row['usertheyfollow']);
}
}
display_data_of_followers($userid);