using multiple statements to get the id from another table - php

I am quite new to programming and am having some problems in trying to get data from a SQL database into my PHP website.
I will make something quite similar to the scenario below.
Let's say my tables include:
pets
pet_id pet_type
1 cat
2 dog
3 rabbit
pet_symptoms
symptom_id symptom
1 not playing
2 not eating
3 not sleeping
...
Action_call
call_id advice
1 take to vet
2 change food
3 give a shower
...
I need my last page to pull session data from the first and second page to get (1) what the problem is with the pet and (2) what pet it is, in order to (3) give the correct advice.
here is my SQL on the 3rd page - calling vet
$rows=$db->query("SELECT call_id, advice pet_id, symptom_id, symptom, pet_type
FROM pets, pet_symptoms, call_vet
WHERE pets.pet_id = pet_symptoms.pet_id
AND pets.pet_type='$_SESSION[pet_type]'
");
the problem I am having is that I want to see the problem as text and not an id on the second page, so I am not sure how to match the ids from different tables according to the problem selected.
thanks for any help
EDIT:
so that would look like
id pet_type pet_symptom
1 cat not sleeping
2 dog not sleeping
.....

CREATE TABLE Pet_health
(Pet_id number(3),
Pet_name Varchar2(15,)
Symptom_id number(3),
Call_id number(3))
Create this table. and this table will get the information from the other 3 table,
you do not have to UPDATE Pet, Pet Symptoms and Action call table unless u want to add new type of pet, new symptom and new advice. do not add duplicates.
Now Pet Health can have all the pets health issues
Once this is done then you can use this SELECT query
SELECT P.pet_type, PH.pet_name, S.symptom, A.advice
FROM Pet_Health PH
INNER JOIN Pets P
ON PH.pet_id =P.pet_id
INNER JOIN Symptom S
ON PH.symptom_id = S.symptom_id
INNER JOIN Action_call A
ON PH.call_id = A.call_id
This query will show everything. if you want to show the ID, you can add it in SELECT statement

Related

MySQL join query duplicates users in output

I have the following tables
ea_users
id
first_name
last_name
email
password
id_roles
ea_user_cfields
id
c_id = custom field ID
u_id = user ID
data
ea_customfields
id
name = name of custom field
description
I want to get all users which have a certain role, but I also want to retrieve all the custom fields per user. This is for the backend of my software where all the ea_users and custom fields should be shown.
I tried the following, but for each custom field, it duplicates the same user
$this->db->join('(SELECT GROUP_CONCAT(data) AS custom_data, id AS dataid, u_id, c_id
FROM ea_user_cfields userc
GROUP BY id) AS tt', 'tt.u_id = ea.id','left');
$this->db->join('(SELECT GROUP_CONCAT(name) AS custom_name, id AS customid
FROM ea_customfields AS cf
GROUP BY id) AS te', 'tt.c_id = te.customid','left');
$this->db->where('id_roles', $customers_role_id);
return $this->db->get('ea_users ea')->result_array();
the problem that u did not understand properly how join works.
its ok, that u have duplicates in select when u have relation one to many.
in few words your case: engine tries to fetch data from table "A" (ea_users) then JOIN according to the conditions another table "B" (ea_customfields). If u have one to many relation between tables (it means that one record from table "A" (lets say that we have in this table A1 record) can contain few related rows in table "B", lets call them as B1.1, B1.2 and B1.3 and B1.4), in this case it will join this records and put join result in memory. So in memory u would see something like
| FromTable A | FromTableB |
| A1 | B1.1 |
| A1 | B1.2 |
| A1 | B1.3 |
| A1 | B1.4 |
if u have 10 records in table "B", which related to the table "A" it would put 10 times in memory copy of data from table "A" during fetching. And then will render it to u.
depending on join type rows, with missing related records, can be skipped at all (INNER JOIN), or can be filled up with NULLs (LEFT JOIN or RIGHT JOIN), etc.
When u think about JOINs, try to imagine yourself, when u try to join on the paper few big tables. U would always need to mark somehow which data come from which table in order to be able to operate with it later, so its quite logically to write row "A1" from table "A" as many times as u need to fill up empty spaces when u find appropriate record in table "B". Otherwise u would have on your paper something like:
| FromTable A | FromTableB |
| A1 | B1.1 |
| | B1.2 |
| | B1.3 |
| | B1.4 |
Yes, its looks ok even when column "FromTable A" contains empty data, when u have 5-10 records and u can easily operate with it (for example u can sort it in your head - u just need to imagine what should be instead of empty space, but for it, u need to remember all the time order how did u wrote the data on the paper). But lets assume that u have 100-1000 records. if u still can sort it easily, lets make things more complicated and tell, that values in table "A" can be empty, etc, etc.. Thats why for mysql engine simpler to repeat many times data from table..
Basically, I always stick to examples when u try to imagine how would u join huge tables on paper or will try to select something from this tables and then make sorting there or something, how would u look through the tables, etc.
GROUP_CONCAT, grouping
Then, next mistake, u did not understand how GROUP_CONCAT works:
The thing is that mysqlEngine fetch on the first step structure into memory using all where conditions, evaluating subqueries + appends all joins. When structure is loaded, it tried to perform GROUPing. It means that it will select from temporary table all rows related to the "A1". Then will try to apply aggregation function to selected data. GROUP_CONCAT function means that we want to apply concatenation on selected group, thus we would see something like "B1.1, B1.2, B1.3, B1.4". Its in few words, but I hope it will help a little to understand it.
I googled table structure so u can write some queries there.
http://www.mysqltutorial.org/tryit/query/mysql-left-join/#1
and here is example how GROUP_CONCAT works, try to execute there query:
SELECT
c.customerNumber, c.customerName, GROUP_CONCAT(orderNumber) AS allOrders
FROM customers c
LEFT JOIN orders o ON (c.customerNumber = o.customerNumber)
GROUP BY 1,2
;
can compare with results with previous one.
power of GROUP in aggregation functions which u can use with it. For example, u can use "COUNT()", "MAX()", "GROUP_CONCAT()" or many many others.
or example of fetching of count (try to execute it):
SELECT c.customerName, count(*) AS ordersCount
FROM customers AS c
LEFT JOIN orders AS o ON (c.customerNumber = o.customerNumber)
GROUP BY 1
;
so my opinion:
simpler and better to solve this issue on client side or on backend, after fetching. because in term of mysql engine response with duplication in column is absolutely correct. BUT of course, u can also solve it using grouping with concatenations for example. but I have a feeling that for your task its overcomplicating of logic
PS.
"GROUP BY 1" - means that I want to group using column 1, so after selecting data into memory mySql will try to group all data using first column, better not to use this format of writing on prod. Its the same as "GROUP BY c.customerNumber".
PPS. Also I read comments like "use DISTINCT", etc.
To use DISTINCT or order functions, u need to understand how does it work, because of incorrect usage it can remove some data from your selection, (same as GROUP or INNER JOINS, etc). On the first look, you code might work fine, but it can cause bugs in logic, which is the most complicated to find out later.
Moreover DISTINCT will not help u, when u have one-to-many relation(in your particular case). U can try to execute queries:
SELECT
c.customerName, orderNumber AS nr
FROM customers c
INNER JOIN orders o ON (c.customerNumber = o.customerNumber)
WHERE c.customerName='Alpha Cognac'
;
SELECT
DISTINCT(c.customerName), orderNumber AS nr
FROM customers c
INNER JOIN orders o ON (c.customerNumber = o.customerNumber)
WHERE c.customerName='Alpha Cognac'
;
the result should be the same. Duplication in customer name column and orders numbers.
and example how to loose data with incorrect query ;):
SELECT
c.customerName, orderNumber AS nr
FROM customers c
INNER JOIN orders o ON (c.customerNumber = o.customerNumber)
WHERE c.customerName='Alpha Cognac'
GROUP BY 1
;

avoid MySQL query from eating unnecessary bandwidth

I am concerned about optimizing my queries as far as the amount of data that is sent back from the database after doing a query.
Let's say that I have 2 tables. One called "artists" and another called "albums".
Let's say "artists" table columns are: id and name
while "albums" table columns are: id, artist_id, title
Let's say that I want a page to diplay the artist's name as the heading. And then below that, I want to display a list of the artist's albums.
I can get that done easily by doing something like:
SELECT artists.name AS artist_name, albums.title AS album_title
LEFT JOIN albums
ON albums.artist_id = artists.id
WHERE artists.id = 3;
This would give me a result that could look something like:
artist_name | album_title
|
Justin Bieber | My First Crappy Album
Justin Bieber | Another Crappy Album
Justin Bieber | Yet Another Crappy Album
The problem with this result is that it gives me back the artist name multiple times, when I only really need it once. I am concerned that this way of doing things is not very efficient. Especially if the artist already has plenty of albums. I could be wrong about this, and if I am, someone please correct me.
In any case, would there be a better way of doing this? One where I don't have to retrieve the artist name multiple times?
You can use the aggregate functions.
SELECT ar.name AS artist_name, GROUP_CONCAT(al.title) AS album_titles
FROM artists ar, albums al
WHERE ar.id = al.artist_id
AND ar.id = 3
GROUP BY artist_name;
This should give you something like:
artist_name | album_titles
Justin Bieber | Album1, Album2, Album3
I haven't used this command in a while, but you can find more documentation on it here: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_group-concat
Also, I prefer to list my tables in the FROM clause and use the WHERE clause to join them, but it's the same as your JOIN above... just a different syntax.
Then do two seperate requests.
First request the artist name
SELECT name
FROM artists
WHERE id = 3
Then the album titles..
SELECT title
FROM albums
WHERE artist_id = 3
I don't know about any bandwith logging inside php, but you can calculate witch way is the fastest in executing using microtime()

MySQL Design Clarification

I'm new to MySQL but have a pretty solid background in a wide variety of programming languages.
Right now I'm designing my first database from the ground up. I understand the basic functionality of MySQL tables and what a relational database is but I'm having trouble wrapping my head around a few things so I'm posting this for help (searching hasn't worked, the terms I've been using are too general and I don't know how to narrow it down). Here's where I'm stuck:
I want to pull Facebook data, specifically interests ("likes") and user location.
If this were some OO language I would just create a data structure for Users with all of the info in it (Facebook ID, interests as an array, location) but I'm not sure how to recreate this in MySQL.
Right now I'm thinking of creating a users table with
Facebook ID (primary key?)
Name
Location
Join date
Then creating an interests table with
Interest name (index, sorted alphabetically?)
Maybe a count of users with this interest
Foreign key that relates back to each user
I think this is where my lack of understanding comes in. How do I replicate the concept of a list or array in MySQL in a way that allows me to map each interest to each user who has "liked" that interest?
Any other suggestions, thoughts, or directions to good tutorial sites are greatly appreciated. I'm a tactile learner so getting my hands dirty with a tutorial would be great for me, I just haven't found one that covers this yet.
You could use a third table that would link the interests table to the user table. There would only be a record if the user liked that interest:
Table User_Interest:
Interest_ID
User_ID
To get a list of a user with all of their interests:
SELECT u.Name, i.Interest_Name
FROM Users AS u
INNER JOIN User_Interest AS ui ON ui.User_ID = u.ID
INNER JOIN interests AS i ON i.ID = ui.Interest_ID
WHERE u.Name = 'Tom Jones'
To get a list of a particular interest and all users that liked:
SELECT u.Name, i.Interest_Name
FROM Users AS u
INNER JOIN User_Interest AS ui ON ui.User_ID = u.ID
INNER JOIN interests AS i ON i.ID = ui.Interest_ID
WHERE i.Interest_Name = 'Hiking'
This type of setup is called a "many to many" relation. To do this, you will need 3 tables. 2 tables which contain your data and the last table a mapping table.
Here is an example of a many to many relation ship where the two objects are Students and Courses
Example
Your user table looks like this:
(ID_User, User_Name, ...)
Your interest table looks like this:
(ID_Interest, Interest_Name, ...)
Now your mapping table will look like this:
(ID_User, ID_Interest)
Sample Data
Now lets put some data in the tables
User_Table
------------------------
ID_user | Username | ...
------------------------
1 John
2 Mark
3 Foo
4 Bar
Interest_Table
----------------------------------
ID_Interest | Interest_Name | ...
----------------------------------
001 Pop
002 Rock
003 Alternative
004 Rap
User_To_Interest (Mapping Table)
---------------------------
ID_user | ID_Interest
---------------------------
1 001
1 003
3 002
2 004
4 001
Examining this
Ok so here if you analyze what has been set up, we are mapping users to interests. The mapping table sort of adds wires from the User object to an interest object.

MySQL: Subqueries for normalized databases

I've spent a lot of time on Google searching for this and also landed on Stackoverflow a few times, but I couldn't figure out how to fulfill my needs.
It might be pretty ease for most of you, but I just can't get it to work.
Let's assume I have this table:
item_data
And another one:
vendor_data
What I want to get is a query where I'm selecting one item from the first table, and the query should replace the vendor_id with the respective name specified in the second table - only for my output of course.
The logic is very easy, I think, but I just can't create a proper query.
Like:
I'm selecting the item with the id 2 from the table item_data, then I get the vendor_id that is also specified in item_data, this vendor_id is being looked up in vendor_data -> vendor_id (3) and the right vendor_name (CompanyZ) is brought back as my result.
I'd really appreciate your help, thanks!
Andy
It sounds like you are just wanting a simple join
SELECT i.item_id, i.item_name, v.vendor_id, v.vendor_name <-- select whatever fields you need here
FROM item_data AS i
INNER JOIN vendor_data AS v ON i.vendor_id = v.vendor.id
WHERE i.item_id = ? <-- specify item_id for lookup here

MySQL - Getting the average in a column

I'm making a list with certain songs. Each song has its own unique ID. OK let's say I have this table called list. A new:
The ID is self-explanatory. It's used to ID rows. song_one through song_ten is filled with a song's unique ID.
Every time a user makes a new list, they add songs and each row gets filled. Now, how would I get the average rank of the songs in the tables list using the song's ID that is filled between song_one through song_ten?
Redesign your database. Make a new table with songid, listid and rank. This will make your job easy.
listsongs
-----------------
songid [PK] -- Foreign key referencing the songs table
listid [PK] -- Foreign key referencing the lists table
rank
Selecting average song ranks:
SELECT
a.song_id
AVG(b.rank) AS avgrank
FROM
songs a
LEFT JOIN
listsongs b PM a.song_id = b.song_id
GROUP BY
a.song_id
Please do as others have said about normalizing your DB structure. If you wish to continue with this design however, you can use this clunky-looking solution:
SELECT
a.song_id,
AVG(
CASE
WHEN a.song_id = b.song_one THEN 1
WHEN a.song_id = b.song_two THEN 2
WHEN a.song_id = b.song_three THEN 3
WHEN a.song_id = b.song_four THEN 4
WHEN a.song_id = b.song_five THEN 5
WHEN a.song_id = b.song_six THEN 6
WHEN a.song_id = b.song_seven THEN 7
WHEN a.song_id = b.song_eight THEN 8
WHEN a.song_id = b.song_nine THEN 9
WHEN a.song_id = b.song_ten THEN 10
END
) AS AvgRank
FROM
songs a
INNER JOIN
list b ON
a.song_id IN
(
b.song_one,
b.song_two,
b.song_three,
b.song_four,
b.song_five,
b.song_six,
b.song_seven,
b.song_eight,
b.song_nine,
b.song_ten
)
GROUP BY
a.song_id
I would listen to #Mark Byers and #Shehzad Bilal, who said that you need to redesign your database structure.
When you think in the terms of tables and their attributes, think logical - think in the terms of code.
For example: If you are writing to a file, would it be easier to create a universal loop to output all the things needed, or to open the file with different pieces of code every time you needed to write something.
In your database, it would be easier to have one table that represents the song itself (that is the general idea behind a database design) than having a table that represents all the songs.
(table) (attribute)
song
id
albumid (fk from table album)
name
title
(...etc)
list
id
songid (fk from table song)
ip
date
(...etc)
If you wanted to create a ranking system, you would do it through code. In some cases, purely depending on your design, you would have a table for it, but it would also be universal.

Categories