mysql group rows in joined query - mysql

Im stumbling upon a problem where i need to retrieve data from the following tables
events
+-------+---------+---------+
| e_id | e_title | e_link |
+-------+---------+---------+
| 1 | Event 1 | event_1 |
| 2 | Event 2 | event_2 |
| 3 | Event 3 | event_3 |
| 4 | Event 4 | event_4 |
| 5 | Event 5 | event_5 |
+-------+---------+---------+
reservations
+-------+---------+---------+
| r_id | r_e_id | r_u_id |
+-------+---------+---------+
| 1 | 2 | 1 |
| 2 | 2 | 3 |
| 3 | 5 | 4 |
| 4 | 2 | 4 |
| 5 | 1 | 1 |
+-------+---------+---------+
users
+-------+---------+----------+
| u_id | u_name | u_gender |
+-------+---------+----------+
| 1 | One | Male |
| 2 | Two | Male |
| 3 | Three | Female |
| 4 | Four | Male |
| 5 | Five | Female |
+-------+---------+----------+
I want to display an event page with the users that are subscribed to that event, like follows:
Event 2
Users:
- One
- Three
- Four
I have the following query with the problem that this one only displays the first user (so in this case Four), which makes sense because the mysql_fetch_assoc() is not in a while() loop.
$result = mysql_query("
SELECT events.e_title, reservations.*, users.u_name
FROM events
JOIN reservations
ON events.e_id = reservations.r_e_id
JOIN users
ON reservations.r_u_id = users.u_id
WHERE events.e_link = '".mysql_real_escape_string($_GET['link'])."'
");
$show = mysql_fetch_assoc($result);
What should i change in my query to make it work the way i want?
EDIT:
The solution from Teez works perfect, but wat if i want to attach more info, say for a link? My desired output is something like this:
Event 2
Users:
- User 1 Male
- User 3 Female
- User 4 Male
How am i going to achieve that? And eventually i even want to split the users by gender. So one list for females and one for males
SECOND EDIT:
I'm stunned with the result so far, but to complete it i want to sort the users by gender, like so:
Event 2
Users male:
- User 1 Male
- User 4 Male
Users female:
- User 3 Female
but how?

Best way will be first make a 2D array containing all events with respective users
Like below:
while( $show = mysql_fetch_assoc($result))
{
$events[$show['e_id']][]=$show['u_name'];
$uid[$show['e_id']][]=$show['u_id'];
}
Then loop arround above array for displaying :
foreach($events ad $key=>$users)
{
echo "Event ".$key."<br>";
echo "Users : <br>";
foreach($users as $ukey=>$name)
{
echo " -<a href='domain.com/user/".$uid[$key][$ukey]."'>".$name."</a>;
}
}

So with each call of mysql_fetch_assoc you want to have the event details and a list of usernames? In MySQL you can use GROUP_CONCAT for this purpose, although it is quite limited and error-prone. You should rather put mysql_fetch_assoc() in a loop to build an array of users. Anyway, here is the GROUP_CONCAT solution:
$result = mysql_query("
SELECT events.e_title, GROUP_CONCAT(users.u_name) e_reservation_users
FROM events
JOIN reservations ON events.e_id = reservations.r_e_id
JOIN users ON reservations.r_u_id = users.u_id
WHERE events.e_link = '".mysql_real_escape_string($_GET['link'])."'
GROUP BY 1
");
$show = mysql_fetch_assoc($result);
$show will then be
array('e_title' => '...', 'e_reservation_users' => '...,...,...').

Related

laravel get row number of a specific record

I have a table queue and I want to know what position or row number a user at.
queue table
----------------------------------------------------
| id | name | created_at | done |
+-----+--------+-------------------------+---------+
| 1 | John | 2020-10-17 01:08:59 | 1 |
| 2 | Jane | 2020-10-17 01:10:15 | 0 |
| 3 | Jess | 2020-10-17 01:18:15 | 0 |
| 4 | Joe | 2020-10-18 08:18:15 | 0 |
| 5 | Moe | 2020-10-18 11:18:15 | 0 |
----------------------------------------------------
is it possible to know the specific number of user in queue? for example Jess will return 3 because he's the 3rd user in the queue record.
edit: for example John is done in the queue, now Jess will become the 2nd in the queue.
I think I have a way:
the main idea is to get the count of the previous queue based on their id values,
and the trick is to use an alias to the main table so you can use that alias in your internal select.
$values = DB::table('queue', 'u1')
->select('u1.id',DB::raw("((SELECT count(*) from queue WHERE queue.id < u1.id)+1) rowNumber"))
->orderBy('u1.id')
->get();
Edit:
if you want to exclude done queue you should do it in the main and the internal select:
$values = DB::table('queue', 'u1')
->select('u1.id',DB::raw("((SELECT count(*) from queue WHERE (queue.id < u1.id)and(queue.is_done!=1) )+1) rowNumber"))
->where('u1.is_done','!=',1)
->orderBy('u1.id')
->get();
If you don't need to get that number directly with query you can do
search()
$collection->search(function ($item, $key) {
return $item->name == 'Jess';
});

One to Many Count with one query?

I haven't touched the backend in a while.. so forgive me if this is super simple. I'm working with Lumen v.5.6.1.
| table.sets | | table.indexed_items |
|----------------| |---------------------------------|
| ID | SET | | ID | setId | itemId | have |
|----|-----------| |----|-------|--------|-----------|
| 1 | set name 1| | 1 | 3 | 1 | 2 |
| 2 | set name 2| | 2 | 3 | 2 | 1 |
| 3 | set name 3| | 3 | 3 | 3 | 4 |
| 4 | 2 | 4 | 1 |
| 5 | 2 | 5 | 3 |
| 6 | 2 | 6 | 1 |
How would I return in one query, groupedBy/distinct by setId (with set name as a left join?) to have a return like this:
[
setId: 2,
name: 'set name 2',
haveTotal: 5,
],
[
setId: 3,
name: 'set name 3',
haveTotal: 7,
]
Here is a raw MySQL query which should work. To convert this to Laravel should not be too much work, though you might need to use DB::raw once or twice.
SELECT
s.ID AS setId,
s.`SET` AS name,
COALESCE(SUM(ii.have), 0) AS haveTotal
FROM sets s
LEFT JOIN indexed_items ii
ON s.ID = ii.setId
GROUP BY
s.ID;
Demo
If you don't want to return sets having no entries in the indexed_items table, then you may remove the call to COALESCE, and you may also use an inner join instead of a left join.
Note that using SET to name your tables and columns is not a good idea because it is a MySQL keyword.
If you are using or want to use eloquent, you can do something like:
$sets = App\Sets::withCount('indexed_items')->get();
This will return a collection with a column name indexed_items_count
Obviously you will need to change depending on your model names.
Here are the docs
I always use in my project for count relation ship record.
$sets->indexed_items->count();

How to properly join two tables to use alternative ORDER BY

Two tables...
people (personid, name, mainordering)
entries (userid, personid, altordering)
"personid" is the common field. My app displays a draggable list users can move around. When done, they click to "lock" in their order.
Table : people
+----------+---------+--------------+
| personid | name | mainordering |
+----------+---------+--------------+
| 1 | Bob | 2 |
| 2 | Charlie | 4 |
| 3 | Jim | 1 |
| 4 | Doug | 3 |
+----------+---------+--------------+
So using mainordering, it would display:
Jim
Bob
Doug
Charlie
entries table might have (for user 16):
+--------+----------+-------------+
| userid | personid | altordering |
+--------+----------+-------------+
| 16 | 1 | 3 |
| 16 | 2 | 1 |
| 16 | 3 | 2 |
| 16 | 4 | 4 |
+--------+----------+-------------+
So if user 16 has already submitted his entry BUT NOT LOCKED IT IN, I want to display his list using altordering. i.e.
Charlie
Jim
Bob
Doug
I'm struggling with the proper join to use. Here is what I tried and isn't working (it's simply ordering by mainordering still)...
$sql = "SELECT * from entries
WHERE userid=".$_SESSION['userid']."
LEFT JOIN people ON entries.personid = people.personid
ORDER BY altordering";
Any thoughts would be much appreciated. Thank you...
Are you sure you don't get an error when using WHERE before JOIN?
It should work like this:
SELECT people.*
FROM people
JOIN entries ON entries.personid = people.personid
WHERE entries.userid={$_SESSION['userid']}
ORDER BY entries.altordering
I assume entries.personid will always have a matching person in people, so you should use an INNER JOIN. You would use FROM entries LEFT JOIN people if you wanted to retrieve altordering even for non-existing people.

Joining from another table multiple times in a MySQL query

I am trying to do multiple joins on the same MySQL table, but am not getting the results that I expect to get. Hopefully someone can point out my mistake(s).
Table 1 - cpe Table
|id | name
|----------
| 1 | cat
| 2 | dog
| 3 | mouse
| 4 | snake
-----------
Table 2 - AutoSelect
|id | name | cpe1_id | cpe2_id | cpe3_id |
|-----------------------------------------------
| 1 | user1 | 1 | 3 | 4 |
| 2 | user2 | 3 | 1 | 2 |
| 3 | user3 | 3 | 3 | 2 |
| 4 | user4 | 4 | 2 | 1 |
------------------------------------------------
I would like to see an output of
user1 | cat | mouse | snake |
user2 | mouse | snake | dog |
..etc
Here is what I have tried
SELECT * FROM AutoSelect
LEFT JOIN cpe ON
( cpe.id = AutoSelect.cpe1_id ) AND
( cpe.id = AutoSelect.cpe2_id ) AND
( cpe.id = AutoSelect.cpe3_id )
I get blank results. I thought i knew how to do these joins, but apparently when I'm trying to match cpe?_id with the name of the cpe table.
Thanks in advance for any assistance.
You need left join 3 times as well. Currently your query only joins 1 time with 3 critieria as to the join. This should do:
SELECT a.name, cpe1.name, cpe2.name, cpe3.name FROM AutoSelect as a
LEFT JOIN cpe as cpe1 ON ( cpe1.id = a.cpe1_id )
LEFT JOIN cpe as cpe2 ON ( cpe2.id = a.cpe2_id )
LEFT JOIN cpe as cpe3 ON ( cpe3.id = a.cpe3_id )
And you probably mean to INNER JOIN rather than LEFT JOIN unless NULL values are allowed in your AutoSelect table.
I think your design is wrong.
With tables like that, you get it the way it's meant to be in relational databases :
table 1 : animal
id name
1 cat
2 dog
3 mouse
4 snake
table 2 : user
|id | name |
|--------------
| 1 | user1 |
| 2 | user2 |
| 3 | user3 |
| 4 | user4 |
table 3 : association
|id_user | id_animal|
|--------------------
| 1 | 1 |
| 1 | 3 |
| 1 | 4 |
| 2 | 3 |
| 2 | 1 |
| 2 | 2 |
| 3 | 3 |
| 3 | 2
| 4 | 4 |
| 4 | 2 |
| 4 | 1 |
---------------------
Then :
select u.name, a.name from user u, animal a, association ass where ass.user_id = u.id and ass.animal_id = a.id;
In this case, your solution won't produce a good dynamic database. There are other ways to make combinations of multiple tables. I can show you by my own database what you should use and when you should use this solution. The scheme is in dutch, but you'll probably understand the keywords.
Like you, I had to combine my windmills with a kWh-meter, which has to measure the energyproduction of my windmills. What you should do, is this case, is making another table(in my case molenkWhlink). Make sure your tables are INNODB-types(for making Foreign keys). What I've done is combining my meters and mills by putting a pointer(a foreign key) of their ID(in Dutch Volgnummer) in the new table. An advantage you may not need, but I certainly did, is the fact I was able to extend the extra table with connection and disconnection info like Timestamps and metervalues when linking or unlinking. This makes your database way more dynamic.
In my case, I Also had a table for meassurements(metingoverzicht). As you can see in the scheme, I've got 2 lines going from Metingoverzicht to molenkwhlink. The reason for this is quite simple. All meassurements I take, will be saved in table Metingoverzicht. Daily meassurements(which are scheduled) will have a special boolean put on, but unscheduled meassurements, will also me saved here, with the bollean turned off. When switching meters, I need the endvalue from the leaving meter and the startvalue from the new meter, to calculate the value of todays eneryproduction. This is where your solution comes in and an extra table won't work. Usually, when you need just one value from another table a JOIN will be used. The problem in this case is, I've got 2 meassurementIDs in 1 link(1 for connecting and 1 for disconnecting). They both point to the same tablecolumn, because they both need to hold the same type of information. That is when you can use a double JOIN from one table towards the other. Because, both values will only be used once, and just needed to be saved in a different place to avoid having 1 action stored on different locations, which should always be avoided.
http://s1101.photobucket.com/user/Manuel_Barcelona/media/schemedatabase.jpg.html

Data Entry Tracking (Database Design)

I have developed a website (PHP) that allow staffs to add records on to our system.
Staffs will be adding thousands of records into our database.
I need a way to keep track of what record have been done and the process/status of record.
Here a number of Teams I could think of:
Data Entry Team
Proof Reading Team
Admin Team
When staff (Data Entry Team) completed a record - he/she will then click on the Complete button. Then somehow it should asssign to 'Proof Reading Team' automatically.
A record need to be checked twice from a Proof Reading Team. If StaffB finish proof reading then another member from Proof Reading Team need to check it again.
When Proof reading is done, Admin Team will then assign "Record Completed"
In a few months later record might need to be updated (spelling mistake, price change, etc) - Admin might to assign record to Data entry team.
Is this good data entry management solution? How do I put this into Database Design perspective?
Here what I tried:
mysql> select * from records;
+----+------------+----------------------+
| id | name | address |
+----+------------+----------------------+
| 1 | Bill Gates | Text 1 Text Text 1 |
| 2 | Jobs Steve | Text 2 Text 2 Text 2 |
+----+------------+----------------------+
mysql> select * from staffs;
+----+-----------+-----------+---------------+
| id | username | password | group |
+----+-----------+-----------+---------------+
| 1 | admin1 | admin1 | admin |
| 2 | DEntryA | DEntryA | data_entry |
| 3 | DEntryB | DEntryB | data_entry |
| 4 | PReadingA | PReadingA | proof_reading |
| 5 | PReadingB | PReadingB | proof_reading |
+----+-----------+-----------+---------------+
mysql> select * from data_entry;
+----+------------+-----------+------------------------+
| id | records_id | staffs_id | record_status |
+----+------------+-----------+------------------------+
| 1 | 2 | 3 | data_entry_processiing |
| 2 | 2 | 3 | data_entry_completed |
| 3 | 2 | 4 | proof_read_processing |
| 4 | 2 | 4 | proof_read_completed |
| 5 | 2 | 5 | proof_read_processing |
| 6 | 2 | 5 | proof_read_completed |
+----+------------+-----------+------------------------+
Is there alternative better solution of database design?
i think design it's well done. but may be you want to separate group into groups table, and record_status into status table. If you're storing a lot of records you would store a lot of useless information, at least create an enum type for record_status field and group field
table: groups
id - name 1 - admin 2 - data_entry 3 - proof_reading
...
table: status
id - name 1 - data_entry_processing ...
and if you want the users to be in different groups at a time, you could create users_group table
table: user_groups
group_id - user_id 1 - 1 2 - 1 1 - 4 3 -
4 4 - 4 ....
Hope this helps