Foreach in a while loop - mysql

I’ve two tables: “cars” and “dealerships”:
Cars table
id_car | name_car | etc.
----------------------------
1 | A3 Sportback | etc.
2 | Ranger | etc.
3 | Transit Van |etc.
4 | Cayman | etc.
etc. | etc. | etc.
Dealerships table
In deal_cars column I insert ids of correspondent cars as array.
deal_id | deal_name | deal_cars | etc.
--------------------------------------
1 | Ford | 2,3 | etc.
2 | Audi | 1 | etc.
3 | Porsche | 4 | etc.
etc. | etc. | etc. |
I’d get a page that displays the following information:
Dealership name – Cars
Ford – Ranger, Transit Van
Audi – A3 Sportback
Cayman – Porsche
I’ve no problem to extract the dealership name, but I don’t know how to extract the car names from ids.
I tried this:
$dealerships_sql = $data->query("SELECT * FROM dealerships ORDER BY deal_name ASC");
while($dealerships_obj = $data->extract($dealerships_sql)){
//Dealerships data
$deal_id[] = $dealerships_obj->deal_id;
$deal_name[] = $dealerships_obj->deal_name;
etc etc
//Try to get cars ids and turn them in cars names.
$deal_cars[] = $dealerships_obj->deal_cars;
$deal_cars[] = explode(',',$deal_cars);
$cars = array();
foreach ($deal_cars AS $deal_car) {
$cars_sql = $data->query("SELECT name_car FROM cars WHERE id_car = '$deal_car'");
while($cars_obj = $data->extract($cars_sql)){
$cars[] = stripslashes($cars_obj->name_car)." ";
}
}
}
I use smarty as template engine so I assign some vars:
$smarty->assign ("deal_id", $deal_id);
$smarty->assign ("deal_name", $deal_name);
etc etc.
$smarty->assign ("cars", $cars);
And my template is:
<table border="1">
<tr>
<td>Dealership</td>
<td>Cars</td>
</tr>
{section name="foo" loop=$deal_name}
<tr>
<td>{$deal_name[foo]}</td>
<td>{$cars[foo]}</td>
</tr>
{/section}
</table>
But code returns:
Ford – Ranger
Audi – A3 Sportback
Cayman – Porsche
It shows only the first car (first element found in array) for each dealership. How can I solve this?

I think your database design does not fit right here.
Cars Table
id_car | name_car | etc.
----------------------------
1 | A3 Sportback | etc.
2 | Ranger | etc.
3 | Transit Van | etc.
4 | Cayman | etc.
etc. | etc. | etc.
Dealer Table
deal_id | deal_name | etc.
---------------------------
1 | Ford | etc.
2 | Audi | etc.
3 | Porsche | etc.
etc. | etc. |
Dealer to Cars Table
dealerid | carid
1 | 2
1 | 3
2 | 1
etc. | etc.
As you see, I did another table for the relationships between the two tables.
$dealerships_sql = $data->query("SELECT * FROM dealerships ORDER BY deal_name ASC");
$dealers = array();
while($dealerships_obj = $data->extract($dealerships_sql)){
//Dealerships data, use object
$dealerid = $dealerships_obj->deal_id;
$dealers[$dealerid]['dealer'] = $dealerships_obj;
// Cars
$cars = array();
$car_sql = $data->query("SELECT name_car FROM cars JOIN dealerToCars ON carid = id_car JOIN dealer ON deal_id = dealerid WHERE deal_id = " . $dealerid);
// now you have all cars from the selected dealer
while ($cars_obj = $data->extract($car_sql))
{
$cars[] = stripslashes($cars_obj->name_car);
}
// Assign Cars to dealer
$dealers[$dealerid]['cars'] = $cars;
}
Now you have in $dealers all your dealers with their cars.
Since you are using smarty, just pass this whole array to smarty and let the rest do smarty in it's template. Smarty is able to use arrays and objects, so you don't need to split it into multiple arrays:
$smarty->assign ("dealers", $dealer);
Because I don't know your classes, you maybe need to add some getters or setters or just make the properties public, so smarty can access them:
<table border="1">
<tr>
<td>Dealership</td>
<td>Cars</td>
</tr>
{foreach $dealers as $dealer}
<tr>
<td>{$dealer['dealer']->deal_name}</td>
<td>{foreach $dealer['cars'] as $car}{$car}, {/foreach}</td>
</tr>
{/foreach}
</table>
Maybe you need to take a look about the identifiers in the template.
(I'm using smarty 3 for that)

I think you should have a dealership row for each car (that will allow you to get all the information with a JOIN and parse it all easily).

The best way would be to get everything at once, you can do it like that:
select
d.deal_name,
group_concat(c.name_car order by c.name_car separator ', ')
from dealerships d
join cars c on find_in_set(c.id_car, d.deal_cars) > 0
group by d.deal_name;
or (as separated fields):
select d.deal_id, d.deal_name, c.id_car, c.name_car
from dealerships d
join cars c on find_in_set(c.id_car, d.deal_cars) > 0;

Related

MySQL table relations - join if field has value

There could be X different tables in my system and one table that stores all relation data of those tables. For example the database could look like this
-------------------------- -----------------------------------
| Table fruits | | Table drinks |
-------------------------- -----------------------------------
| id | name | | id | name |
-------------------------- -----------------------------------
| 1 | banana | | 1 | strawberry apple cocktail|
| 2 | apple | | 2 | banana juice |
| 3 | strawberry | | 3 | cola |
-------------------------- -----------------------------------
--------------------------
| table food |
--------------------------
| id | name |
--------------------------
| 1 | apple pie |
--------------------------
And the table for the relations contains the id and the table name from both entries.
--------------------------------------------------
| table relations |
--------------------------------------------------
| id | table_1 | id_1 | table_2 | id_2 |
--------------------------------------------------
| 1 | fruits | 1 | drinks | 2 | banana + banana juice
| 2 | fruits | 2 | drinks | 1 | apple + s.a. cocktail
| 3 | fruits | 3 | drinks | 1 | strawberry + s.a cocktail
| 4 | food | 1 | fruits | 2 | apple pie + apple
| 5 | drinks | 3 | food | 1 | if someone wants to mix it
--------------------------------------------------
If I have a drink (strawberry apple cocktail) I want to get all fruit ids that belong to it (strawberry and apple) and the other way around when I have a fruit (strawberry) I would like to query for all related drink ids.
So I need one query structure to find both sides and since it is all dynamic I can't know if the fruit is in table_1 or table_2 (otherwise it would be simple)
I would like to have something like this (attention pseudocode) to query all ids from drinks that contain bananas (table: fruits, id: 1)
SELECT id
FROM drinks
LEFT JOIN relations AS rel ON drinks.id = (rel.table_1 == drinks)? rel.id_1 : rel.id_2
WHERE
((rel.id_1 = '1' AND rel.table_1 = 'fruits') OR ( rel.id_2 = '1' AND rel.table_2 = 'fruits' ))
My issues
When I join my relation table only one time, I would have to query two times since I don't know if I have to join it on id_1 or id_2
When I do 2 Joins like this
Select * from drinks
LEFT JOIN relations AS relation1 ON drinks.id = relation1.id_1
LEFT JOIN relations AS relation2 ON drinks.id = relation2.id_2
WHERE
( (relation1.id_1 = '1'
AND relation1.table_1 = 'fruits')
OR ( relation2.id_2 = '1'
AND relation2.table_2 = 'fruits' )
OR ( relation1.id_2 = '1'
AND relation1.table_2 = 'fruits')
OR ( relation2.id_1 = '1'
AND relation2.table_1 = 'fruits'))
I can search for everything from both sides (have drinks -> receive fruits / have fruit -> receive drinks) but I get also the drink with the same Id like the fruit
I would like to avoid to select * from relations since I build the query with php with other conditions like fruit name, dateCreated and so on and I would have to restructure the entire query if it contains relations
You can download the mysql dump here to test it. I hope someone could be so kind to help me.
Thank you and sry for my bad english
Your problem is in your design, the drink table has many-to-many relation with fruit table, so you should separate fruit_drink_relation's table.
The same many-to-many relation exist for fruit_drink_food_relation's table.
To look for food, query the second relation's table, otherwise, the first relation table is enough.
Utterly bad design there.
See, the Relational Database Management Systems possess the powerful mechanism of keeping and enforcing consistent (!) relationships between entities (represented as records in the tables).
Why not use it as it is?
You got n..n (many-to-many) relationships, not more than that, as seen from examples.
There's a more-than-common pattern for it, the relation table that essentially consists of two columns both of which are foreign keys to the entities you'd need to connect together:
CREATE TABLE fruits_juices (
fruit_id INT,
juice_id INT
FOREIGN KEY fruit_id
REFERENCES fruits(id),
FOREIGN KEY juice_id
REFERENCES juices(id)
)
Do that for each relation you need - and this it.
And that is nothing bad in creating a couple of relationships - that's the way RDBMS supposed to work (hence what they're best at).
Don't try to be unnecessary versatile: your use cases don't demand that whatsoever.
Too one-for-all solutions are usually not good in all the cases they suppose to cover and have an additional maintenance cost to keep them from falling apart (data consistency, in your case)
I think you want a snowflake schema.
Looks like currently you are -in the ugly- half way of that.

how to retrieve one to many relationship data in mysql

I have two table
table#1
name_id | name
--------|-------
1 | abc
2 | def
table#2
subject_id| name_id |subject
----------|---------|--------
1 | 1 |malayalam
2 | 1 | english
3 | 1 | hindi
4 | 2 | malayalam
5 | 2 | hindi
i want to join these two tables and get output in the following format
id |name |subject1 |subject2 |subject3
---|-----|--------- |---------|--------
1 |abc |malayalam |english |hindi
2 |def |malayalam |hindi |
is it possible in mySql?
can anyone help?
I'll leave my other answer up because again, I think you should really consider redesigning your DB schema. You're using MySQL so have you tried using group_concat() ? It's not going to put it in table form like that but will give you the expected data, where the different subjects will be in CSV form.
SELECT one.name_id as id, name, group_concat(subject)
FROM one
INNER JOIN two
ON id = two.name_id
GROUP BY id;
Output
1 | abc | malayalam, english, hindi
2 | def | malayalam, hindi
So I'm not exactly sure about the scope of your schema here, because you have a subject_id that doesn't correlate to each subject. For example, subject_id 1 = malayalam but malayalm also equals subject_id 3, but subject_id 3 also equals hindi, while again hindi is also assigned to subject_id 4. Very confusing schema to work with.
You mention it's a one to many, but without knowing more context I don't see that being the case. A person can have more than one subject. One subject can belong to more than 1 person. This is a many to many relationship, which means you should be using 3 tables here.
For the sake of your question, you can use an inner join
SELECT one.name_id as id, name, subject
FROM one
INNER JOIN two
ON id = two.name_id
ORDER BY id;
The output of that query would be:
1 | abc | malayalam
1 | abc | english
1 | abc | hindi
2 | def | malayalam
2 | def | hindi
However, I would highly consider re-designing your database. I mean you want your data in that format, which looks nice with only 3 subjects. But what happens when there's 50 subjects for one person? Would you still want all subjects on one line like that? It's better to break it up, it will save you down the road when your database starts becoming more complex. In many cases, to create a many to many relationship you'll want a 3rd table that connects the two entities, in this case I'd create an Enrolled_Classes table to connect Names and Subjects
Names
name_id | name
--------------
1 | abc
2 | def
Subjects
subject_id | subject
--------------------
1 | malayalm
2 | english
3 | hindi
Enrolled_Classes
name_id | subject_id
--------------------
1 | 1
1 | 2
1 | 3
2 | 1
2 | 3
Then you'd need another join for your query:
SELECT name, subject
FROM Names
INNER JOIN Enrolled_Classes
ON Names.name_id = Enrolled_Classes.name_id
INNER JOIN Subjects
ON Enrolled_Classes.subject_id = Subjects.subject_id;
Same Output
abc | malayalam
abc | english
abc | hindi
def | malayalam
def | hindi

MYSQL (WHERE - IF - SELECT/IN statement)

I'm trying to set my code up where I have a Control Panel for my Sales Reps.
From my Control Panel, I am able to control which customers a Rep calls based on the item the customer bought.
i.e. If I assign "Cars" in my Control Panel for Sales Rep 01, he would only call customers that bought toys in the "Cars" category. And all different toys in my "Cars" category are stored in a separate file named "Vehicles".
My files are as follows:
customer (File)
+--------+-----------+--------+
| name | phone | toy |
+--------+-----------+--------+
| Gail | 777-1234 | Truck |
| June | 777-1235 | Doll |
| Mary | 777-1236 | Racer |
| Bill | 777-1237 | Ball |
| Jon | 777-1238 | Jeep |
+--------+-----------+--------+
control_panel (File)
+----------+--------+
| user | desc |
+----------+--------+
| sales_01 | Cars |
+----------+--------|
vehicles (File)
+---------+
+ item |
+---------+
| Truck |
| Racer |
| Jeep |
+---------+
In trying to test this code out, I have this portion of my code working.
select
c.name , c.phone
FROM
customer c
WHERE
c.toy IN (
SELECT
v.item
FROM
vehicles v
)
Now I'm trying to condition my WHERE statement so that only if I choose "Cars" in my Control Panel screen for User "sales_01", then customers who bought Cars will only show on the Call Screen for User "sales_01".
This is one example of some of the code I've been testing but cant get to work correctly.
SELECT
c.name , c.phone
FROM
customer c , control_panel p
WHERE
(IF p.desc = "Cars"
THEN (c.toy
IN (SELECT
v.item
FROM
vehicles v)
)
END)
Any help is appreciated. Thx.
To start with, I suggest you refactor your schema by adding an field category to your vehicles table. This will allow a proper relationship between your control_panel and customer. In that case you could just do SELECT c.name, c.phone FROM customer c LEFT OUTER JOIN vehicles v ON c.toy = v.items WHERE v.category = 'Cars'. I would have loved to add other suggestions but I feel you want something that could work right now. Plus, I do not have much time to spare presently. Hope this helps.

Split SQL field containing array into new table/rows

I need a list of user IDs (course_user_ids) that is currently stored in a single field of a larger table.
I have a table called courses that contains course information with course_id and course_students as such:
-----------------------------------------------------------
| course_id | course_students |
----------------------------------------------------------
| 1 | a:3:{i:0;i:12345;i:1;i:22345;i:2;i:323456;} |
-----------------------------------------------------------
| 2 | a:32:{ … } |
-----------------------------------------------------------
The course_students part contains 3 chunks of information:
the number of students (a:3:{…) -- not needed
the order/key for the array of each student ({i:0;… i:1;… i:2; …}) -- also not needed
the course_user_id (i:12345; … i:22345;… i:32345;)
I only need the course_user_id and the original course_id, resulting in a new table that i can use for joins/subqueries like this:
------------------------------
| course_id | course_user_id |
------------------------------
| 1 | 12345 |
------------------------------
| 1 | 22345 |
------------------------------
| 1 | 323456 |
------------------------------
(ideally able to continue to break out values for other course_ids and course_user_ids, but not a priority:)
| … | … |
------------------------------
| 2 | … |
------------------------------
| 2 | … |
------------------------------
| 97 | … |
------------------------------
| 97 | … |
------------------------------
| … | … |
------------------------------
Note: the course_user_id can vary in length (some are 5 digits, some are 6)
Any ideas would be much appreciated!
Update
My user table does have user_id which can be mapped to course_students or course_user_id, so that is a very helpful observation from below.
I also think I need to use a LEFT JOIN because some students are registered in multiple courses, and I'd like to see each instance/combo.
Let us assume that you have a table name users which contains all users data along with user_id.
Now you can join table courses and table users in following manner:
select c.course_id,u.user_id
from
courses c
join users u
on u.user_id=if(instr(c.course_students,concat(":",u.user_id,";"))>0,u.user_id,c.course_students)
You get the result as per your requirement.
Verify at http://sqlfiddle.com/#!9/3667d/2
Note: The above query works fine if no overlapping between user_id and array index. In case of overlapping, kindly filter data using where-clause
If I got your goal correctly you have users table. And {i:0;i:12345;i:1;i:22345;i:2;i:323456; equal users.id=12345,users.id=22345 etc.
If my guess is correct you can try this solution:
http://sqlfiddle.com/#!9/cfef27/5
SELECT * FROM courses
LEFT JOIN users u
ON courses.course_students LIKE CONCAT('%i:',u.id,';%')

How to determine a number of grouped records in Rails?

A table has this structure and data in there:
id | car | location | ...
-----------------------------
1 | BMW | US | ...
2 | Audi | US | ...
3 | Audi | US | ...
4 | Porsche | CA | ...
5 | Audi | US | ...
I am loading and grouping data in a usual way:
#cars = Cars.where('location = ?', location).group('car')
I'd need to display in a view a number of grouped data - in this case, I would need to print how much of data is in the US for example; so the desired output would be like:
BMW - 1 car
Audi - 3 cars
How to get a number of grouped rows?
Car.where(location: location).group(:car).count #=> e.g. {'BMW' => 1}
Just FYI, you can also group by location AND car.
Car.group(:location, :car).count #=> e.g. {['US', 'BMW'] => 1}