Using two columns with the same foreign key - mysql

I have a database, which has 5 tables, but I have problem. In the shipment table, there are two columns, source and destination, which are both foreign key references to the route table, but when I select a record from the shipment table it will only show the same routename for both.
Here is the code:
SELECT fare, commission, driver,shipment._date, routename, vehiclenumber, productname, source, destination, routename,ownername
FROM route, shipment, product, vehicle,owner
WHERE vehicle.vehicleid = shipment.vehicle
AND shipment.source
and vehicle.owner=owner.ownerid
AND shipment.destination = route.routeid
AND shipment.product = product.productid
AND vehicle.vehiclenumber = 'nk-234'
ORDER BY _date
LIMIT 0 , 30

To connect a record in one table (one shipment) to more than one record in another table (two routes) you would ideally use explicit JOINs against the route table twice (or more, for however many links you happen to need).
Here's a quick modification to your query that demonstrates usage. Pay particular attention to the two routename columns in the SELECT and the two JOINs to the route table in the FROM:
SELECT fare,
commission,
driver,
shipment._date,
RS.routename, <-- field from first join
vehiclenumber,
productname,
source,
destination,
RD.routename, <-- field from second join
ownername
FROM shipment
JOIN route RS ON RS.routeID = shipment.source <-- join 1; source
JOIN route RD ON RD.routeID = shipment.destination, <-- join 2; destination
product,
vehicle,
owner
WHERE vehicle.vehicleid = shipment.vehicle
AND vehicle.owner = owner.ownerid
AND shipment.product = product.productid
AND vehicle.vehiclenumber = 'nk-234'
ORDER BY _date
LIMIT 0 , 30
Obviously, this might not match your needs exactly, because I don't know what tables the other selected fields are from to be able to make sure they are all accounted for...

This is because you select the fields from one row in shipment, in orer to actually get any effect from your foreign keys you need to use a few joins
http://dev.mysql.com/doc/refman/5.5/en/join.html

Using the old-style (non-preferred) notation, you can write:
SELECT s.fare, s.commission, s.driver, s._date, v.vehiclenumber, p.productname,
s.source, s.destination, rs.routename, rd.routename, o.ownername
FROM route AS rs, route AS rd, shipment AS s, product AS p, vehicle AS v, owner AS o
WHERE s.vehicleid = s.vehicle
AND s.source = rs.routeid
AND v.owner = o.ownerid
AND s.destination = rd.routeid
AND s.product = p.productid
AND v.vehiclenumber = 'nk-234'
ORDER BY _date
LIMIT 0, 30
I had to guess which table(s) contain the fare, commission and driver information; I assumed they are all in the shipment table.
Starting a column name with an underscore is not strictly allowed in standard SQL, though most DBMS do allow it. It looks ugly, though; why not 'ship_date' as the column name?
It is better to use the explicit join notation, not least because there is less chance of forgetting a join condition:
SELECT s.fare, s.commission, s.driver, s._date, v.vehiclenumber, p.productname,
s.source, s.destination, rs.routename, rd.routename, o.ownername
FROM shipment AS s
JOIN route AS rs ON s.source = rs.routeid
JOIN route AS rd ON s.destination = rs.routeid
JOIN vehicle AS v ON s.vehicleid = s.vehicle
JOIN owner AS o ON v.owner = o.ownerid
JOIN product AS p ON s.product = p.productid
WHERE v.vehiclenumber = 'nk-234'
ORDER BY s._date
LIMIT 0, 30

Related

How do I get the MIN value from two columns along with corresponding id?

My query does return the lowest price from the two columns (price_base, price_special) but it is not returning the correct store_id that corresponds to the lowest price found.
My Query:
SELECT grocery_item.id, grocery_item.category,
grocery_category.name AS cat, grocery_item.name AS itemName,
MIN( if( grocery_price.price_special>0,
grocery_price.price_base)) AS price,
grocery_price.store_id,
grocery_store.name AS storeName
FROM grocery_item
LEFT JOIN grocery_category ON
grocery_category.id=grocery_item.category
LEFT JOIN grocery_price
ON grocery_price.item_id = grocery_item.id
LEFT JOIN grocery_store
ON grocery_store.id=grocery_price.store_id
WHERE grocery_price.selection='no'
AND buy='yes'
GROUP BY grocery_price.item_id
ORDER BY store_id, grocery_item.category, grocery_item.name
Returns this:
ID category cat itemName price store_id storeName
92 3 Bread/Bakery Arnold Bread 2.14 1 Food Lion
But the grocery_price table holds this info:
item_id price_base price_special store_id
92 4.29 2.14 9
92 3.99 0.00 1
so the store_id I need to be returned is 9 (the storeName returned would NOT then be Food Lion)
EDIT: WORKING QUERY based on Uueerdo's comments (thank you!)
SELECT minP.item_id, gi.category, gc.name AS cat,
gi.name as itemName, gp.store_id,
gs.name AS storeName, minP.price
FROM
(SELECT p.item_id, MIN(IF(p.price_special >0,
p.price_special,p.price_base)) AS price
FROM grocery_item AS i
INNER JOIN grocery_price AS p ON (i.id = p.item_id)
WHERE i.buy = 'yes'
GROUP BY p.item_id) AS minP
INNER JOIN grocery_item AS gi ON minP.item_id = gi.id
INNER JOIN grocery_category AS gc on gi.category = gc.id
LEFT JOIN grocery_price AS gp
ON minP.price = IF(gp.price_special > 0,
gp.price_special,gp.price_base)
AND gp.item_id = gi.id
INNER JOIN grocery_store AS gs ON gp.store_id = gs.id
GROUP BY gi.id
ORDER BY gs.id, gi.category,gi.name
The values returned for non-grouped, non-aggregated fields are an (effectively) random selection from the values encountered with the grouped fields' values. Most RDBMS do not even consider such a query valid, and even newer versions of MySQL default to disallowing such queries.
In cases like yours, where you need the non-grouped value(s) associated with the aggregate result (min in this case); the aggregating query must be converted into a subquery, that can be joined back to the aggregated tables to find the source row(s) that correspond to the aggregated value.
Edit: Basically, you need to look at the problem slightly differently. You're currently finding the lowest price for an item and a store that item is listed for; you need to find the lowest price for an item, and use that to find the store(s) that have the item at that price.
This gets you the lowest price for item's marked "buy":
SELECT p.item_id, MIN(IF(p.price_special > 0,p.price_special,p.price_base)) AS price
FROM grocery_item AS i
INNER JOIN grocery_price AS p ON (i.id = p.item_id)
WHERE i.buy = 'yes'
GROUP BY p.item_id
You can then take that to get the rest of the results:
SELECT minP.item_id
, gi.name
, gi.category, gc.category_name AS cat, gi.Name as itemName, gi.buy
, gp.store_id, gp.name AS storeName
, minP.price
FROM ([the query above]) AS minP
INNER JOIN grocery_item AS gi ON minP.item_id = gi.id
INNER JOIN grocery_category AS gc on gi.category = gc.grocery_category_id
/* Guessing on this join since grocery_category_id
was not qualified with it's table name */
INNER JOIN grocery_price AS gp
ON minP.price = IF(gp.price_special > 0,gp.price_special,gp.price_base)
/* Alternatively: ON minP.price IN (gp.price_special, gp.price_base)
... though this could cause false positives if the minP.price is 0
from one store's base price being "free"
*/
INNER JOIN grocery_store AS gs ON gp.store_id = gs.id
;

Count(*) as inside a JOIN on SQL Query

I have the following query for a report all is working fine, but I need to add a variable in the report that totals up the number of records for each record returned based off the number of records in the "manheim_auction_listings" record. I feel like it needs to be inside a join but everywhere I would the "COUNT(*) AS num_of_runs" it seems to make the whole query only return a single line with the count the total number of records in the query rather than all the lines with a variable num_of_runs with the number of "manheim_auction_listings" records for each CAR record.
SELECT products.client_id,
clients.name AS client_name,
manheim_auction_lanes.lane_number,
manheim_auction_listings.sequence,
manheim_auction_listings.gross_sale_price,
products.asking_price, products.asking_price_condition,
manheim_auctions.auction_date,
manheim_auctions.auction_number,
product_purchases.total_spent,
product_purchases.purchase_price
FROM manheim_auction_listings
JOIN cars ON
cars.id = manheim_auction_listings.car_id
JOIN products ON
cars.product_id = products.id
JOIN product_purchases ON
current_product_purchase_id = product_purchases.id
JOIN manheim_auctions ON
manheim_auctions.id = manheim_auction_listings.manheim_auction_id
JOIN manheim_auction_lanes ON
manheim_auction_lanes.id = manheim_auction_listings.manheim_auction_lane_id
JOIN clients ON
clients.id = products.client_id
AND clients.id LIKE $P{LoggedInUserAttribute_ClientID}
WHERE
manheim_auctions.auction_number = $P{SaleNumber}
AND manheim_auctions.`year` = $P{SaleYear}
ORDER BY manheim_auction_lanes.lane_number DESC,
manheim_auction_listings.sequence DESC
Please try the following...
SELECT products.client_id,
clients.name AS client_name,
manheim_auction_lanes.lane_number,
manheim_auction_listings.sequence,
manheim_auction_listings.gross_sale_price,
num_of_runs,
products.asking_price, products.asking_price_condition,
manheim_auctions.auction_date,
manheim_auctions.auction_number,
product_purchases.total_spent,
product_purchases.purchase_price
FROM ( SELECT manheim_auction_id AS manheim_auction_id,
COUNT( manheim_auction_id ) AS num_of_runs
FROM manheim_auction_listings
GROUP BY manheim_auction_id
) AS num_of_runs_finder
JOIN manheim_auction_listings ON manheim_auction_listings.manheim_auction_id = num_of_runs.manheim_auction_id
JOIN cars ON cars.id = manheim_auction_listings.car_id
JOIN products ON cars.product_id = products.id
JOIN product_purchases ON current_product_purchase_id = product_purchases.id
JOIN manheim_auctions ON manheim_auctions.id = manheim_auction_listings.manheim_auction_id
JOIN manheim_auction_lanes ON manheim_auction_lanes.id = manheim_auction_listings.manheim_auction_lane_id
JOIN clients ON clients.id = products.client_id
AND clients.id LIKE $P{LoggedInUserAttribute_ClientID}
WHERE manheim_auctions.auction_number = $P{SaleNumber}
AND manheim_auctions.`year` = $P{SaleYear}
ORDER BY manheim_auction_lanes.lane_number DESC,
manheim_auction_listings.sequence DESC
This works by joining your other tables to one that calculates the number of listings associated with each manheim_auction_id, effectively appending a manheim_auction_id's count to each row where that manheim_auction_id occurs.
If num_of_runs is calculated on some other criteria, then please advsie me accordingly.
If you have any questions or comments, then please feel free to post a Comment accordingly.

MySQL, select, comparing values from 2 tables

in my database I want to select every construction_manual
where storage_room.quantity > component.quantity
But when I use:
SELECT construction_manual.name
FROM construction_manual cm, construction_manual_component cmc, component, storage_room
WHERE cm.ID = cmc.construction_manual_ID
AND cmc.component_ID = component.ID
AND component.storage_room_ID = storage_room.ID
AND storage_room.quantity > component.quantity
It will select every construction_manual.name as long as the first component in the storage_room has enough quantity.
So let's say...
construction_manual.1 needs
component.A -> quantity 5
component.B -> quantity 10
and in the storage room there is:
component.A -> quantity 6
component.B -> quantity 0
Although there is not enough in the storage room, construction_manual.1 will be selected. How do I select only those construction_manuals where there are enough components in the storage_room?
edit:
In my 4 tables are the following datasets. I will get the following result when I use the query mentioned above:database . But I should not be able to construct a table because there are not enough wooden_plates (wooden planks and not wooden plates ofc...ups)
I think what you want to ask is to ignore or exclude any construction manual that does not have enough components to satisfy a build. If that's the case you can do something like the below (a left join would also do it):
select distinct cm.name as construction_manual
from construction_manual cm
join construction_manual_component cmc
on cmc.construction_manual_ID = cm.ID
join component c
on c.ID = cmc.component_ID
join storage_room sr
on sr.ID = c.storage_room_ID
where sr.quantity > c.quantity
and cm.ID not in (select cm.ID
from construction_manual cm
join construction_manual_component cmc
on cmc.construction_manual_ID = cm.ID
join component c
on c.ID = cmc.component_ID
join storage_room sr
on sr.ID = c.storage_room_ID
where sr.quantity < c.quantity);

Issues with a MySQL JOIN statement

i have 5 tables called personal,dailypay,bonuses,iou and loans am trying to write a query that will generate payroll from this table's...my code is
select personal.name as NAME,
(sum(dailypay.pay) + bonuses) - (iou.amount + loans.monthly_due)) as SALARY
from personal
join dailypay on personal.eid = dailypay.eid
left join bonuses on personal.eid = bonuses.eid
left join iou on personal.eid = iou.eid
left join where dailypay.date = 'specified_date'
and bonuses.date_approved = 'specified_date'
and iou.date_approved = 'specified_date'
and loans.date = month(now()
It returns the name and null salary values for staffs that does have records for either bonuses,iou and loans. But i want to sum their dailypay, deduct/add deductions or additions return the values, in the event of no record it should proceed with the summation without any deduction or subtraction.
You missed something when pasting the code, as there is no join for the loans table. Also, you are using the table bonuses as a value, you need a field name also. I added some code for the join and for the field, but used ??? for names that are unknown to me.
When you add or subtract a null value to something else, the result is null, that's why you get null as result when any of the values from the left-joined tables are missing. You can use ifnull(..., 0) to turn a null value into zero.
You need a group by clause, otherwise it would sum up the salary for all persons.
If I get you right, you have several records in the dailypay table for each user, but only one record per user in the other tables? In that case you have the problem that you will be joining the other tables against each row in the dailypay, so if you have 20 payment records for a user, it will count the bonus 20 times. You can use an aggregate like max to get the value only once.
You have put conditions for the left.joined tables in the where clause, but this will turn the joins into inner joins. You should have those conditions in each join clause.
select
personal.name as NAME,
(sum(dailypay.pay) + ifnull(max(bonuses.???), 0)) - (ifnull(max(iou.amount), 0) + ifnull(max(loans.monthly_due), 0)) as SALARY
from
personal
inner join dailypay on personal.eid = dailypay.eid
left join bonuses on personal.eid = bonuses.eid and bonuses.date_approved = 'specified_date'
left join iou on personal.eid = iou.eid and iou.date_approved = 'specified_date'
left join loans on personal.??? = loans.??? and loans.date = month(now())
where
dailypay.date = 'specified_date'
group by
personal.name
There seems to be an extranous left join before the where and a missing closing bracket ) in month(now()
so it should look like:
select personal.name as NAME,
(sum(dailypay.pay) + bonuses) - (iou.amount + loans.monthly_due)) as SALARY
from personal
join dailypay on personal.eid = dailypay.eid
left join bonuses on personal.eid = bonuses.eid
left join iou on personal.eid = iou.eid
where dailypay.date = 'specified_date'
and bonuses.date_approved = 'specified_date'
and iou.date_approved = 'specified_date'
and loans.date = month(now())

How can I take the results of two database rows and condense them into a single row?

I have a mysql db query that I'd like to take and condense into a single row. The output is currently two rows and will only ever be two rows. The reason I am getting two rows in the first place is because there will always be a primary and a secondary phone number associated with my result.
Since all of the data is exactly the same for both rows with the exception of the phone type (ptype) and second phone number, I'd like to just take those two columns and append them to the forst row.
I know this can be done but I can't remember how to do it.
Here is the code I am using:
SELECT
c.name,
c.address1,
p.ptype,
p.phone
FROM
customer AS c
Inner Join customeremp AS e ON c.customer_id = e.customer_id
Inner Join customerphone AS p ON e.customer_seq = p.customer_seq
WHERE
c.customer_id = '1'
You need to create 2 joins on the phone table
SELECTc.name,
c.address1,
p.ptype,
p.phone,
p2.ptype,
p2.phone,
FROMcustomer AS cInner
Join customeremp AS e ON c.customer_id = e.customer_idInner
Join customerphone AS p ON e.customer_seq = p.customer_seq and p.pType = 1 (or whatever private means)
Join customerphone AS p2 ON e.customer_seq = p2.customer_seq and p2.pType = 2 (or whatever the other type is)
WHEREc.customer_id = '1'
Edit: this query will only return customer which have BOTH phone types set. If they are optional, then you should consider changing the join into an outer join
select c.name,c.address1,primary.phone,secondary.phone
from customer c
join customeremp e
on c.customer_id = e.customer_id
join customerphone primary
on primary.ptype = 'primary'
and e.customer_seq = primary.customer_seq
join customerphone secondary
on secondary.ptype = 'secondary'
and e.customer_seq = secondary.customer_seq
where c.customer_id = '1'
Try this:
SELECT
c.name,
c.address1,
IF(p.ptype = 'primary', p.phone, null)),
IF(p.ptype = 'secondary', p.phone, null))
FROM customer JOIN (blah blah)
WHERE customer_id = 1
GROUP BY c.name, c.address1
If it doesn't work, you might need to put an aggregate function on the last two fields, eg: MAX(IF(p.ptype ...