How to join all tables? - php

I am a new programmer and I don't know how to do this.
I have:
a table named customers (cust_id, name)
a table named agents (agent_id, agent_name)
a table named authorizations (cust_id, agent_id)
a table named product_services (p_s_id, cust_id, agent_id, item_name, item_id)
About the authorizations table: the agent_id is the agent who the is authorised salesperson for cust_id. So, I need to be able to pull only results for this particular cust_id under this agent_id.
I need to do a query that will return all the products and services that the customer have under all other agents. In the search, it should also return products/services that they have under me as well.
This is what I have tried so far:
$sql = "SELECT
product_services.item_name, agents.agent_name,
customers.name, agents.agent_id, product_services.item_id
FROM
product_services
LEFT JOIN agents ON product_services.agent_id = agents.agent_id
LEFT JOIN customers ON product_services.cust_id = customers.cust_id
WHERE authorizations.agent_id = '$aid'
AND product_services.item_name LIKE '%$q%'
ORDER BY product_services.id DESC
LIMIT $start, $limit";

KPO,
Below is a query I used to connect 2 tables. It would be the same for your query, notice the syntax used starting at "LEFT JOIN"
SELECT
user.username,
groups.group_id, groups.group_name,
sign.last_connected, sign.sign_id, sign.sign_name, sign.resolution_x, sign.resolution_y, LEFT(sign.sign_name, 1) AS first_char
FROM
user
LEFT JOIN(
groups, sign
)ON(
user.user_id = groups.userID AND
groups.group_id = sign.groupID
)
WHERE
username = ? AND
UPPER(sign.sign_name) BETWEEN "A" AND "Z"
OR sign.sign_name BETWEEN "0" AND "9" ORDER BY sign.sign_name
Considering your SQL query is initially correct this should work for your LEFT JOIN portion:
LEFT JOIN(agents, customers
)ON( product_services.agent_id = agents.agent_id
AND product_services.cust_id = customers.cust_id)

Typically, a query would start with the core table at the root of your criteria... Such as a single customer and join from that... You also state you want ALL products/services from ALL agents including the "me" agent, you would not want to have a filter on your '$aid' criteria.
SELECT STRAIGHT_JOIN
c.cust_id,
c.name,
ag.agent_id,
ag.agent_name,
ps.item_name,
ps.p_s_id,
ps.item_id
from
customers c
join authorizations au
on c.cust_id = au.cust_id
[[ AND au.agent_id = '$aid' ]]
join agents ag
on au.agent_id = ag.agent_id
join product_services ps
on c.cust_id = ps.cust_id
AND au.agent_id = ps.agent_id
[[ AND ps.item_name like '%$q%' ]]
order by
ps.p_s_id DESC
limit
$start, $limit
In the query above, I've put in section where you could apply your
[[ AND agent criteria ]]
or product/service criteria. But as you stated, you wanted ALL activity of ALL agents, so I've left it out... Likewise with a possible
[[ AND product / service ]]
criteria which may/may not be provided and you can very simply strip it out...
-- EDIT PER COMMENT FEEDBACK.
As per your inquiry on how to add more "criteria", its based on the origin of the table. If its the FIRST table in the "FROM" clause, you'll add a WHERE clause and put criteria to that table... As for the others, like the agents and product services, where I had the [[ criteria ]], you could just expand your entire criteria there (per respective table its joined with).
Such as your [[ product service criteria ]], I could have added something like
AND ( ( ps.Item like '%$something'
OR ps.Item like '%$another'
OR ps.Item like '%$more' )
AND ps.OtherField = whatever )
Keep your primary "join" conditions which identify the relationship between the tables first and foremost... only THEN do you want to add your restricting criteria... As you can see in my sample above, I've wrapped the entire AND ( ) clause within parenthesis to see it as a single "unit" condition... such as to the product/service table.
Hope this sample helps you in future querying.

Related

Distinct Values from MySQLi Query

I am trying to only show unique userIds (userIds are (0,1,2,3,4,5,6,7,8,9 etc...) for the query I am running. I tried using DISTINCT in my query, but it only shows me unique values of the rows that have 2 or more of the same userId.
Is there a way I can use php to only show the unique values. My weak points are arrays and it makes it more complicated because its using data from a MySQLi query.
Example right now I have with the query now (lets say its GROUP BY rentPaid DESC and the rent total is 800.00 for all users):
userID rentPaid rentMonth
2--------800.00------April
1--------500.00------April
3--------400.00------April
3--------400.00------April
1--------200.00------April
1--------100.00------April
Example desired output:
userID rentPaid rentMonth
2--------800.00------April
1--------500.00------April
3--------400.00------April
Can I do this with MYSQL because I tried DISTINCT and it wouldn't work, how about PHP?
Query:
SELECT
properties.*,
leases.*,
users.userId, users.primaryPhone,
CONCAT(users.userFirstName,' ',users.userLastName) AS user,
admins.adminName, payments.*
FROM
properties
LEFT JOIN leases ON properties.propertyId = leases.propertyId
LEFT JOIN assigned ON properties.propertyId = assigned.propertyId
LEFT JOIN admins ON assigned.adminId = admins.adminId
LEFT JOIN users ON properties.propertyId = users.propertyId
LEFT JOIN payments ON properties.propertyId = payments.propertyId
WHERE
payments.rentMonth = '$currentMonth' AND
payments.rentYear = '$currentYear'
Edit: Please excuse my formatting, this is my first post.
Edit: Added query....its long, but works lol. I only want unique userIds (no double or triple userIds etc...)
I suspect this is what you want:
SELECT userID, MAX(rentPaid) AS maxRentPaid, rentMonth
FROM yourTable
WHERE rentMonth = "April"
GROUP BY userID
ORDER BY maxRentPaid

Select similiar address records based on three columns(city+streetaddress+state) value along with join operation in mysql

"SELECT family.* ,charity.charityName FROM family join charity WHERE charity.user_id = family.createdby AND family.streetAddress in (SELECT family.streetAddress FROM family GROUP BY family.city,family.streetAddress HAVING count(*) > 1 AND ) ORDER BY family.streetAddress ASC LIMIT $offset,$limit"
this Query produces following result as shown in image
https://drive.google.com/file/d/0B3RNacAE6rR5Rk8tUUI2Q3B3X3M/view?usp=sharing
blue marked is problem record that should not come.
Above Query list all records with similiar street address but failed to get only those records with similiar address && city. I need to get only those records which are having same city and streetaddress . is there any way to apply and logic for groupby
Your main select's WHERE clause only checks for a matching address :
AND family.streetAddress in (....)
which will match any address found in your subselect, even if it belongs to the wrong city. The subselect currently only returns the address, but not the matching city. That's the problem.
I'm not sure that this will fix your problem, but you can try to can change the subselect into a nested table expression and match on both address and city. Maybe something along the lines of
SELECT family.* --and other main select stuff
WHERE charity.user_id = family.createdby
AND exists --similar to count(*) > 1
select streetAddress, city
from (
(SELECT family.streetAddress, family.city
FROM family
GROUP BY family.city,family.streetAddress
HAVING count(*) > 1) AS subSelect
)
WHERE family.streetAddress = subSelect.streetAddress
AND family.city = subSelect.city

Creating complex IF, OR, NOT & AND SQL queries (MySQL)

(Apologies if this a duplicate - I have tried searching, but I may not know the right word for what I'm trying to achieve - feel free to correct me!)
The Background
So I have a PHP based app (Codeigniter, but I'm using normal SQL language for this part), that has a MySQL database, with 2 tables - 'contact' and 'order'.
For simplicity, let's assume that:
'contact' has 3 cols : Id, FirstName, LastName
'order' has 4 cols : Id, ContactId, ItemBought, ItemValidDate
Example of a row in 'order' table: 22, 11, Adult Membership, 2012/13
Id is primary key of both tables, ContactId is foreign key for 'contact' and ItemBought and ItemValidDate are both simple varchar (we're storing 'seasons' rather than dates -I know, its not ideal but its what the client wants)
At some point, I know, I am going to have to extend this for 3 tables and use an OrderItem table, to allow an order to have multiple items, so I'd like to find a solution that can be built on. But at present, I don't even understand the basics so I've kept it to 2 tables
The Problem
I want to create a search page that allows the user to find subsets of records based on lots of different criteria.
See screenshot of search page
This form submits as an array of criteria like this:
[order_type_operator] => Array
(
[0] => equal
[1] => equalor
[2] => notequal
)
[order_type] => Array
(
[0] => Adult Membership
[1] => Adult Membership
[2] => Adult Membership
)
[order_expire] => Array
(
[0] => 2005/06
[1] => 2006/07
[2] => 2010/11
)
[submit] => Start Search
I then cycle through this array, testing to see if values have been submitted, and build up my SQL query.
So, I hope I've explained it properly, so that its clear a user may use this form to search for records that match lots of different conditions - in theory, unlimited numbers of conditions - to end up with a list of contacts that match this criteria.
What I have Tried
Example 1 - simple WHERE
"find contact records that have an order record for 'Adult Membership' in '2009/10'"
i.e. SELECT * FROM contact
JOIN order ON contact.Id = order.ContactId WHERE (order.ItemBought = 'Adult Membership' AND order.ItemValidDate = '2009/10')
This works fine.
Example 2 - WHERE OR WHERE
"find contact records that have an order record for 'Adult Membership' in '2009/10'" OR have a an order record for 'Adult Membership' in '2010/10'
i.e. SELECT * FROM contact
JOIN order ON contact.Id = order.ContactId WHERE (order.ItemBought = 'Adult Membership' AND order.ItemValidDate = '2009/10') OR (order.ItemBought = 'Adult Membership' AND order.ItemValidDate = '2010/11')
This works fine as long as EVERY condition the user is asking for is an OR query. I assume that I can build this query up using brackets and OR for as big as I like? E.g. find Adult membership in 2005/06, OR 2006/07, OR 2007/08, OR 2008/09 etc etc will be just like the above SQL with lots more brackets joined by 'OR'?
Example 3 - WHERE AND WHERE - I'm stuck!
"find contact records that have an order record for 'Adult Membership' in '2009/10' OR 2010/11 AND have a an order record for 'Adult Membership' in '2012/13'
At the moment, I've been trying UNION, however if there are more queries to follow this (e.g Adult membership in 2008 OR 2009 AND 2010) this means doing more than one SELECT. (Perhaps this is the answer?)
e.g. `SELECT * FROM contact
JOIN order ON contact.Id = order.ContactId WHERE (order.ItemBought = 'Adult Membership' AND order.ItemValidDate = '2009/10') OR (order.ItemBought = 'Adult Membership' AND order.ItemValidDate = '2010/11')
UNION
SELECT * FROM contact
JOIN order ON contact.Id = order.ContactId WHERE (order.ItemBought = 'Adult Membership' AND order.ItemValidDate = '2012/13)`
Example 4 - But does NOT have a record.... Blows my mind
"find contact records that have an order record for 'Adult Membership' in '2009/10' AND have an order record for 'Adult Membership' in '2010/10' BUT DO NOT have an order of 'Sponsorship' in 2007/08
I wondered about running these queries, storing the results in a PHP array and then doing a IN (*array of ids already selected*), but this just seems like I'm not using SQL properly.
So clever people - what am I doing wrong?
Thank you so much in advance for you help.
PS. Not asking you write the code for me!
PPS. If you know of any good tutorials then I'll happily follow them!
PPPS. If this is a duplicate, then please accept my apologies!
As ZorleQ says it can rapidly get to be a mess
For your 3rd question a possible solution using joins of subselects would be as follows
SELECT contact.*, order.*
FROM contact
INNER JOIN order
ON contact.Id = order.ContactId
INNER JOIN (SELECT DISTINCT ContactId
FROM order
WHERE (ItemBought = 'Adult Membership' AND ItemValidDate = '2009/10')
OR (ItemBought = 'Adult Membership' AND ItemValidDate = '2010/11')) Sub1
ON contact.Id = Sub1.ContactId
INNER JOIN (SELECT DISTINCT ContactId
FROM order
WHERE (ItemBought = 'Adult Membership' AND ItemValidDate = '2012/13')) Sub2
ON contact.Id = Sub2.ContactId
You could probably do this without using the subselects and just a plain join as follows
SELECT contact.*, order.*
FROM contact
INNER JOIN order
ON contact.Id = order.ContactId
LEFT OUTER JOIN order Sub1
ON contact.Id = Sub1.ContactId AND Sub1.ItemBought = 'Adult Membership' AND Sub1.ItemValidDate = '2009/10'
LEFT OUTER JOIN order Sub2
ON contact.Id = Sub2.ContactId AND Sub2.ItemBought = 'Adult Membership' AND Sub2.ItemValidDate = '2010/11'
INNER JOIN order Sub3
ON contact.Id = Sub3.ContactId AND Sub3.ItemBought = 'Adult Membership' AND Sub3.ItemValidDate = '2012/13'
WHERE Sub1.ContactId IS NOT NULL OR Sub2 IS NOT NULL
Your 4th question can be done using a LEFT OUTER JOIN to find a record with Sponsorship bought for 2007/08, and only returning rows where a match isn't found (ie, check the ContactId on the LEFT OUTER JOINed table is NULL).
SELECT contact.*, order.*
FROM contact
INNER JOIN order
ON contact.Id = order.ContactId
INNER JOIN order Sub1
ON contact.Id = Sub1.ContactId AND Sub1.ItemBought = 'Adult Membership' AND Sub1.ItemValidDate = '2009/10'
INNER JOIN order Sub2
ON contact.Id = Sub2.ContactId AND Sub2.ItemBought = 'Adult Membership' AND Sub2.ItemValidDate = '2010/10'
LEFT OUTER JOIN order Sub3
ON contact.Id = Sub3.ContactId AND Sub3.ItemBought = 'Sponsorship' AND Sub3.ItemValidDate = '2007/08'
WHERE Sub3.ContactId IS NULL
I think you have to take a step back and try to visualize your question on paper first. Examples 1 and 2 are pretty easy, but let's look at example 3.
For conditions where all your criteria are 'AND' or 'OR' - things are very simple. Just do a long WHERE, just liek before.
However, when you start mixing them you have to answer yourself a serious question:
How do you split the conditions?
Lets say someone picked up those criteria:
and A
or B
and C
This gives you so many permutations of your query! eg:
(A or B) and C
A or (B and C)
(A and C) or B
If you add one more 'OR' to it, you will end it with tens of combinations more! Leaving you in a place where you have to guess what to do. Don't even want to think what would happen if there is a NOT involved...
This is not a direct answer to your question, but more of a pointer towards a possible solution. The last time we had to do something similar, we've ended up grouping the conditions together into blocks.
You could either add a condition within a block or add a new search block. Think of the blocks as brackets in the example above. Everything in a block is an 'AND' or 'NOT AND', and between blocks you can specify 'and' or 'or'. This way you know straight away how to structure your query. This worked like a charm in a standalone application. Might be a bit tricky to implement it nicely on a page, but you catch the idea.
My solution to all issues like this where multiple criteria may or may not be provided by the user is the following... (this example is for oracle, but should be able to be done in MySQL as well)...
You pass in all the filter variables, regardless of whether they are null or filled with a value. In this example, I'll say I have 3 values the user may or may not fill that act as filters on the SELECT.
SELECT
*
FROM
table
WHERE
NVL2(InputVariable1, InputVariable1, Column1) = Column1
OR NVL2(InputVariable2, InputVariable2, Column2) = Column2
OR NVL2(InputVariable3, InputVariable3, Column3) = Column3
NVL2 - This is an oracle function. If the first value is not null, it returns the second value, otherwise it returns the third value. If you aren't using oracle, and there is no equivalent function for NVL2, simply write the function yourself.
So, using the above example, the code ALWAYS passes all three InputVariables into the select statement, even if they are NULL. By using NVL2 or an equivalent function, the comparison is between the InputVariable and the Column ONLY if the InputVariable is not null; otherwise it is between the Column and the Column, which will of course always be true, thereby effectively ignoring that filter variable, which is what you want (i.e. a null filter value matches all rows - i.e. if user does not specify LastName, then include all LastNames).
This solution allows you to use many filter variables without having to do a lot of processing up front - just pass them all down into the SELECT every time, whether they are null or not.
If you have sets of filter variables (i.e. the user enables a set of input values via a checkbox or some similar mechanism), you can do the above inside of a CASE statement. Each case should check the enable value for a given set, and return the result of evaluating the entire set of filter variables (exactly like the above). You then compare the result of the entire CASE structure to 1, as in...
WHERE
CASE [ expression ]
WHEN enableSet1
THEN NVL2(InputVariable1, InputVariable1, Column1) = Column1
OR NVL2(InputVariable2, InputVariable2, Column2) = Column2
OR NVL2(InputVariable3, InputVariable3, Column3) = Column3
WHEN condition_2 THEN result_2
...
WHEN condition_n THEN result_n
END = 1
This works because the value of a CASE structure is the result of the THEN block which was evaluated.
This will allow you to do ALL or MOST of your desired filtering within the confines of a single SELECT statement - again, without having to do a lot of pre-processing to build the SELECT.

How do I echo out a name -only if- specific rows in one table match specific rows in another based on an id?

Here's my problem: I'm making a crafting system for a game, and I already have my database filled with information for resources required to craft items.
Here are what my relevant tables look like:
table #edible_resources
(edible_resource_id, edible_resource_name, hunger_points, degeneration_id)
table #edible_ground
(id, resource, amount, location)
table #req_crafting_edible
(req_crafting_edible_id, edible_resource_id, req_resource_amount, created_item_id)
table #items
(item_id, item_name, degeneration_id, is_grounded, is_stackable, can_equip, can_edit)
What I want to do is -only- echo out the craftable item's name if, and only if -all- required resources (and their required amounts) are on the ground in the location of the character.
I have a query that comes close:
SELECT items.item_name, items.item_id FROM items
INNER JOIN req_crafting_edible
ON req_crafting_edible.created_item_id = items.item_id
INNER JOIN edible_ground
ON edible_ground.resource = req_crafting_edible.edible_resource_id
AND edible_ground.amount >= req_crafting_edible.req_resource_amount
WHERE edible_ground.location = $current_location
GROUP BY items.item_name
ORDER BY items.item_name
But this shows me craftable items regardless if I have ALL the required items in the area. It shows me items as long as I have -one- of their required resources.
Is there a way to only show the name of a craftable item only if I have -all- the required resources (and their amounts) in edible_ground where location = $current_location?
For more information on what I've tried:
$get_char = mysql_query("SELECT current_char FROM accounts WHERE account_id ='".$_SESSION['user_id']."'");
$current_char = mysql_result($get_char, 0, 'current_char');
$get_loc = mysql_query("SELECT current_location FROM characters WHERE character_id = $current_char");
$current_location = mysql_result($get_loc, 0, 'current_location');
//---------------------------------------------------------------COOKED FOOD
$get_food = mysql_query("SELECT items.item_name, items.item_id FROM items
INNER JOIN req_crafting_edible
ON req_crafting_edible.created_item_id = items.item_id
INNER JOIN edible_ground
ON edible_ground.resource = req_crafting_edible.edible_resource_id
AND edible_ground.amount >= req_crafting_edible.req_resource_amount
WHERE edible_ground.location = $current_location
GROUP BY items.item_name
ORDER BY items.item_name");
while ($food = mysql_fetch_array($get_food)){
echo $food['item_name'].'<br>';
}
This returns:
Baked Fish
Charred Fish
Fish Soup
Glazed Berry
Cake
Grilled Fish
Sashimi
Seafood Soup
Sushi
Udon
On the ground:
1 fish
1 honey
Even though fish soup, berry cake, udon etc needs much more than just the one fish that's in the area.
Can anyone help me figure this out? I'd be forever grateful; I've spent a few days already trying to myself. Please?
And before anyone says anything, I know I need to start using mysqli; unfortunately I didn't even realize it existed when I started to make the game (and learn PHP at the same time months ago), so I'll have to painfully go back and change it all in an update.
You want a HAVING clause to check the count of the records you are grouping through the INNER JOINs.
HAVING count(*) = (
SELECT count(*)
FROM req_crafting_edible
WHERE req_crafting_edible.created_item_id = items.item_id
)
Edit:
So basically you need to know two pieces of information:
How many different resources are required
Do each of those resources have the required amounts
The first is solved by the sub query above.
Your query as-is satisfies the second point but only for 1 resource.
HAVING basically does some special magic on your group clause. HAVING count(*) means there are X records being grouped together. Because of how the join works, you will have 1 item.name for each resource. The sub select gives you the count of how many different resources, and therefore grouped records, are needed for that item. Comparing that sub query with the count(*) of the grouping ensures you have all the needed resources.
And here is the final query, modifying your code above:
SELECT items.item_name, items.item_id
FROM items
INNER JOIN req_crafting_edible
ON req_crafting_edible.created_item_id = items.item_id
INNER JOIN edible_ground
ON edible_ground.resource = req_crafting_edible.edible_resource_id
AND edible_ground.amount >= req_crafting_edible.req_resource_amount
WHERE edible_ground.location = $current_location
GROUP BY items.item_name
HAVING count(*) = (
SELECT count(*)
FROM req_crafting_edible
WHERE req_crafting_edible.created_item_id = items.item_id
)
ORDER BY items.item_name
You only actually want the data from the items table, right? If so I would move to using an exists model:
SELECT I.item_name, I.item_id FROM items I
WHERE NOT EXISTS
(SELECT created_item_id
FROM req_crafting_edible R
WHERE R.created_item_id = I.item_id
AND NOT EXISTS
(SELECT G.resource
FROM edible_ground G
WHERE G.resource = R.edible_resource_id
AND edible_ground.location = $current_location
AND G.amount >= R.req_resource_amount))
ORDER BY I.item_name
I don't have your database to check this, but the logic goes like this:
Find the items that don't have any unsatisfied requirements.
Find the unsatisfied requirements for the current item. (IE. Find
requirements that don't have resources on the ground)
Find the edible resources that match the current requirement, are at
this location, and have enough.
I don't work in mysql as much at the moment, but let me know if this doesn't work.

PDO perform 2 mysql selects in the same loop

quick question!
how do i perform 2 selects with PDO and run them through the same loop ?
$sql1 = $db->query("SELECT * FROM produkts");
$sql2 = $db->query("SELECT * FROM buyers");
$sql1->setFetchMode(PDO::FETCH_ASSOC);
$sql1->setFetchMode(PDO::FETCH_ASSOC);
while($row = $stmt->fetch() && $row1 = $stmt1->fetch()){
Something to be displayed
}
This obviously doesn't work, and i have no clue how to make it work.
how is this performed ??
Assuming something like:
Buyers Produkts
---------- ---------
buyerid .-produktid
produktid-' [...]
[...]
You can use:
SELECT b.*, p.*
FROM buyers b
LEFT JOIN produkts p
ON p.produktid = b.produktid
That will go through and query each buyers then for every produktid supplied and found in the produkts table, it will be joined in the result. You can also reverse it it bring buys in on products: For example:
SELECT p.*, b.*
FROM produkts p
LEFT JOIN buyers b
ON b.produktid = p.produktid
Which will bring in the buyers details based on a produkts match.
This is going by what you've told me, that both tables share a produktid column. I still don't know what you're after or know any more about the table schema, so if I'm off let me know (and provide more information).

Categories