SELECT JOIN MYSQL - php

I have 2 tables which is userand userdata.
Once a customer buy a product, it will insert into table userdata and user.
In user table , the product id is recorded in user.buyid in form of "id, id ,id ,id ,id".
example : "23, 24". So I have tried to get all rows with id ( 23, 24 ) from product by using these queries
SELECT * FROM `userdata`,`user` WHERE `userdata`.`id` IN (`user`.`buyid`);
Query return 1 row product which is id=23
SELECT * FROM `user` INNER JOIN `userdata` ON `userdata`.`id` IN (`user`.`buyid`);
This too return only 1 row of product with id 23.
Expected result each row return this:
user.id | user.name | user.contact | product.id | product.name
1 | nazmi | 99999 | 23 | A
1 | nazmi | 99999 | 24 | B

Fix your data model! Storing lists of ids in a string is bad, bad, bad:
Your id is an integer. It should be stored as a number, not a string.
Your id refers to another table. It should have properly declared foreign key relationships.
A column should contain a single column.
SQL has a great way of storing lists. Really powerful. It is called a "table" not a "string column".
String processing capabilities are pretty poor in SQL.
Queries with such data structures cannot take advantage of indexes and partitioning.
Sometimes, you are stuck with other people's really, really, really bad decisions. MySQL offers a nice function (too nice in my opinion) to help:
SELECT *
FROM user u JOIN
userdata ud
ON find_in_set(ud.id, u.buyid) > 0;
If you have spaces in your id list, then you need to get rid of them. And the separator has to be a comma.
SELECT *
FROM user u JOIN
userdata ud
ON find_in_set(ud.id, replace(u.buyid, ' ', '')) > 0;

Related

How to fetch data from database comparing some value using PHP and Mysql

I have some doubt. Actually i need to fetch data as per column value by joining two table using PHP and MySQL .I am explaining my tables below.
db_specials:
id special type
1 Food and Beverage 1
2 Liquor 2
db_basic:
id special_id name
1 2 aaa
2 1 Raj
3 2 Ram
4 2 Jay
5 1 Nikesh
Here i need to fetch all data from the tables db_basic those are associated with Liquor from first table. I am explaining my code below.
$res=mysqli_query($connect,"select b.id,b.special_id,b.name,s.type from db_basic as b inner join db_specials as s on b.special_id=s.id where b.special_id=2 order by b.id desc");
while($row=mysqli_fetch_array($res)){
$data[]=$row;
}
I am getting also the proper data. Here problem is db_specials also has the delete functionality on front end with insert. Suppose user deleted the row which has id=2 in db_specials table and insert again,in this case the id will change 2 to 3. So the query also needs to change accordingly. Here i need to know what should be the best logic so that each time user will not change the query if any specials is deleted and inserted again. Please help me.
Instead of id column, you should rely on the type column of db_specials table, since it's not going to be auto incremented for each INSERT operation. So your query should be like this:
select b.id,b.special_id,b.name,s.type
from db_basic as b
inner join db_specials as s
on b.special_id=s.type
where b.special_id=2
order by b.id desc
Also, I recommend that you should change the special_id column name of db_basic table to type, like this:
+------+------+------+
| id | type | name |
+------+------+------+
| | | |
This way, it would be easier for you to construct the query, like this:
select b.id,b.type,b.name
from db_basic as b
inner join db_specials as s
on b.type=s.type
where b.type=2
order by b.id desc
if user deleted 2,Liquor,2 in db_specials and reinsert, you cannot control what the user input in the front end.
User might be insert Food,2
Instead of join the type column of db_specials with db_basic, you should put a checking in the front end, when user click the delete button, prompt a message before delete 2,Liquor,2 from the table db_specials, if there is any special_id=2 in table db_basic the corresponding rows will deleted as well.

GROUP_CONCAT with ordering and missing fields

I have a series of tables that I want to get rows returned from in the following format:
Student ID | Last Name | First Name | Quiz Scores
-------------------------------------------------
xxxxxxx | Snow | Jon | 0,0,0,0,0,0,0,0
There's 3 relevant tables (changing any existing DB structure is not an option):
person - table of all people in the organization
enrollment - table of student and faculty enrollment data
tilt.quiz - table of quiz scores, with each row storing an individual score
The tricky part of this is the Quiz Scores. A row for the quiz score only exists if the student has taken a the quiz. Each quiz row has a module, 1 - 8. So possible quiz data for a student could be (each of these being a separate row):
person_id | module | score
---------------------------
223355 | 1 | 100
223355 | 2 | 95
223355 | 4 | 80
223355 | 7 | 100
I need the quiz scores returned in proper order with 8 comma separated values, regardless if any or all of the quizzes are missing.
I currently have the following query:
SELECT
person.id,
first_name,
last_name,
GROUP_CONCAT(tilt.quiz.score) AS scores
FROM person
LEFT JOIN enrollment ON person.id = enrollment.person_id
LEFT JOIN tilt.quiz ON person.id = tilt.quiz.person_id
WHERE
enrollment.course_id = '$num' AND enrollment_status_id = 1
GROUP BY person.id
ORDER BY last_name
The problems with this are:
It does not order the quizzes by module
If any of the quizzes are missing it simply returns fewer values
So I need the GROUP_CONCAT scores to at least include commas for missing quiz values, and have them ordered correctly.
The one solution I considered was creating a temporary table of the quiz scores, but I'm not sure this is the most efficient method or exactly how to go about it.
EDIT: Another solution would be to execute a query to check for the existence of each quiz individually but this seems clunky (a total of 9 queries instead of 1); I was hoping there was a more elegant way.
How would this be accomplished?
There are some assumptions here about your data structure, but this should be pretty close to what you're after. Take a look at the documentation for GROUP_CONCAT and COALESCE.
SELECT `person`.`id`, `person`.`first_name`, `person`.`last_name`,
GROUP_CONCAT(
COALESCE(`tilt`.`quiz`.`score`, 'N/A')
ORDER BY `tilt`.`quiz`.`module_id`
) AS `scores`
FROM `person`
CROSS JOIN `modules`
LEFT JOIN `enrollment` USING (`person_id`)
LEFT JOIN `tilt`.`quiz` USING (`person_id`, `module_id`)
WHERE (`enrollment`.`course_id` = '$num')
AND (`enrollment`.`enrollment_status_id` = 1)
GROUP BY `person`.`id`
ORDER BY `person`.`last_name`
First thing to do is use the IFNULL() function on the score
Then, use ORDER BY inside the GROUP_CONCAT
Here is my proposed query
SELECT
person.id,
first_name,
last_name,
GROUP_CONCAT(IFNULL(tilt.quiz.score,0) ORDER BY tilt.quiz.module) AS scores
FROM person
LEFT JOIN enrollment ON person.id = enrollment.person_id
LEFT JOIN tilt.quiz ON person.id = tilt.quiz.person_id
WHERE
enrollment.course_id = '$num' AND enrollment_status_id = 1
GROUP BY person.id
ORDER BY last_name

Joining multiple tables with NULL values to avoid duplication

I am building a news feed from multiple tables status, events and tracks. The data retrieved from these tables should correspond to the user-id of all the users that I follow. On the face of it I thought this seemed simple enough and I could do this with a few joins.
Every row in each of the status, events and tracks table has unique ID and they are also unique from each other, this should make matters easier later. I have done this using a unique_id table with a primary key to retrieve ID's before inserting.
My trouble is upon joining everything together the values duplicate.
Example
If I have this data.
----------
**Status**
user-id = 1
id = 1
status = Hello Universe!
----------
**Events**
user-id = 1
id = 2
event-name = The Big Bang
----------
**Tracks**
user-id = 1
id = 3
track-name = Boom
----------
Assuming I follow user 1 I would want to retrieve this.
user-id ---- id ---- status ---- event-name ---- track-name
1 1 Hello NULL NULL
Universe
1 2 NULL The Big Bang NULL
1 3 NULL NULL Boom
But in reality what I would get is something like this.
user-id ---- status.id ---- events.id ---- tracks.id ---- status ---- event-name ---- track-name
1 1 2 3 Hello The Big Bang Boom
Universe
And that row would be repeated 3 times.
Most of the queries I have tried will get something along those lines.
SELECT * FROM users
INNER JOIN followers ON users.id = followers.`user-id`
LEFT JOIN status ON followers.`follows-id` = status.`user-id`
LEFT JOIN events ON followers.`follows-id` = events.`user-id`
LEFT JOIN tracks ON followers.`follows-id` = tracks.`user-id`
WHERE users.`id` = 2
I am using laravel, so eventually this query will be put into Eloquent format. If there is a simpler and a not performance degrading way of doing this in Eloquent please let me know.
Edit
I cannot use a UNION as there is a different number of values in each table. The example is simplified for ease of reading.
Thanks to Frazz for pointing out I could use UNIONS. I have researched into them and come up with this query.
SELECT stream.*, users.id AS me FROM users
INNER JOIN followers ON users.id = followers.`user-id`
LEFT JOIN (
SELECT `id`,`user-id`,`created_at`, `name`, NULL as status
FROM events
UNION ALL
SELECT `id`,`user-id`, `created_at`,NULL AS name, `status`
FROM status
) AS stream ON stream.`user-id` = `followers`.`follows-id`
WHERE users.id = 2
Now comes the process of converting it to an eloquent model...

PDO and prepared statements on dynamic sized queries

I am developing a small gaming website for college fest where users attend few contests and based on their ranks in result table, points are updated in their user table. Then the result table is truncated for the next event. The schemas are as follows:
user
-------------------------------------------------------------
user_id | name | college | points |
-------------------------------------------------------------
result
---------------------------
user_id | score
---------------------------
Now, the first 3 students are given 100 points, next 15 given 50 points and others are given 10 points each.
Now, I am having problem in developing queries because I don't know how many users will attempt the contest, so I have to append that many ? in the query. Secondly, I also need to put ) at the end.
My queries are like
$query_top3=update user set points =points+100 where id in(?,?,?);
$query_next5=update user set points = points +50 where id in(?,?,?,?,?);
$query_others=update user set points=points+50 where id in (?,?...........,?);
How can I prepare those queries dynamically? Or, is there any better approach?
EDIT
Though its similar to this question,but in my scenario I have 3 different dynamic queries.
If I understand correctly your requirements you can rank results and update users table (adding points) all in one query
UPDATE users u JOIN
(
SELECT user_id,
(
SELECT 1 + COUNT(*)
FROM result
WHERE score >= r.score
AND user_id <> r.user_id
) rank
FROM result r
) q
ON u.user_id = q.user_id
SET points = points +
CASE
WHEN q.rank BETWEEN 1 AND 3 THEN 100
WHEN q.rank BETWEEN 4 AND 18 THEN 50
ELSE 10
END;
It totally dynamic based on the contents in of result table. You no longer need to deal with each user_id individually.
Here is SQLFiddle demo

Joining 2 Tables and show the same column repeatedly based on its associative ID

Hi i am not sure how to put this in a brief sentences, but i have DB table like the following
User Table
user_id
username
and so on...
Item
item_id
item_name
Item_Equipped
equipped_id head (FK to item_id)
hand (FK to item_id)
user_id (FK to user_id IN User Table)
I would like to generate a query that will display like the following format
user_id | head | head_item_name | hand | hand_item_name | ...
So far i only able to do this:
SELECT user.user_id, user.username,
equipments.head, equipments.head_acc,
equipments.hand,
equipments.acc, equipments.body
FROM gw_member_equipped AS equipments
LEFT JOIN gw_member AS user ON user.memberid = equipments.member_id
Which (i have to be brutally honest) doesn't do anything much.
I tried to perform INNER JOIN between item and item_equipped however i am unable to get individual name for each item (based on its item ID)
you need to join ITEM table two times with ITEM_EQUIPPED table.
you can use below query for your desired output column shown in question..
SELECT USER.User_Id,
Item_Equipped.Head,
Item_Heads.Item_Id Head_Item_Id, -- if you want you can remove this column
Item_Heads.Item_Name Head_Item_Name,
Item_Equipped.Hand,
Item_Hands.Item_Id Hand_Item_Id, -- and this column also as these columns are same as their previous select columns
Item_Hands.Item_Name Hand_Item_Name
FROM USER, Item Item_Heads, Item Item_Hands, Item_Equipped
WHERE USER.User_Id = Item_Equipped.User_Id
AND Item_Heads.Item_Id = Item_Equipped.Head
AND Item_Hands.Item_Id = Item_Equipped.Hand

Categories