Selecting all rows from one table where condition in joined table - mysql

I have a Products table and a ProductionReport table in my database.
The ProductionReport table captures weekly production information for each product. My app will report on this information.
Table structures
ProductionReport
productionreportid, productid, date, qty
1, 1, 2013-04-08, 50
2, 2, 2013-04-08, 12
Products
productid, productname
1, Skin cream
2, Peanut Oil
3, Bath Salts
4, Shampoo
My page uses a gridview that will list all products (SELECT productid, productname FROM Products) and join in the ProductionReport table so as to display a list of all products and production values for the week that user can update.
My problem is the sql query to populate the GridView. It currently only gets rows where there is a joined value in both tables:
SELECT pro.productname, pr.productionreportid, IFNULL(pr.qty, 0) qty
FROM Products pro
LEFT JOIN ProductionReport pr ON pro.productid = pr.productid
WHERE DATE(pr.date) = '2013-04-08'
So, given the above described data, I'm expecting to get the following resultset back
Skin Cream, 1, 50
Peanut Oil, 2, 12
Bath Salts, 0, 0
Shampoo, 0, 0
Unfortunately, all I'm getting back is the first 2 rows. I believe that's because of the WHERE clause targeting a column in the joined table. If that's the case, it's clearly a serious flaw in my logic, but I don't know what to do about it.
Any suggestions?

Try this
SELECT
pro.productname,
pr.productionreportid,
IFNULL(pr.qty, 0) qty
FROM
Products pro
LEFT JOIN ProductionReport pr
ON pro.productid = pr.productid
AND DATE(pr.date) = '2013-04-08'
Basically move the date condition from WHERE clause to the JOIN clause

You are correct in that DATE(pr.date) = '2013-04-08' won't match if pr.date is null.
You can change it to something like pr.date is null or date(pr.date) = '2013-04-08'

Related

Query for showing stock as of given date in the past

I am developing software for warehouse management.
I got three tables: itemstock, documents, doc_pcs
itemstock fields (not all of them are shown):
rfid_no (which is unique and primary key)
item_name
status
documents fields:
id
doc_date
doc_type
doc_pcs fields:
id
id_doc | these two are primary key
pos | (id_doc is connected to documents.id)
rfid_no (from itemstock.rfid_no)
First table is for collecting records of items, nothing is ever erased from this table, there's just status change when something happens with particular item (goes out to client, comes back etc).
Documents and doc_pcs tables store document data.
doc_type is for marking whether items on document were going in or out of the warehouse.
The problem is:
I need to create query that shows all the stock as of given date.
In other words:
It should show all items from today's itemstock with status=6 ("stored") MINUS records with rfid_no that have matches in doc_pcs related to documents dated between today and a given date and with doc_type="out" PLUS records with rfid_no that have matches in doc_pcs related to documents dated between today and a given date and with doc_type="in"
Was searching for any clue here and there for few days now and I could not find anything that will lead me to any solution. I will be gratefull for any help!
Sample data:
itemstock (as of today):
00300D0909DA, "tshirt", 6
00300D0909DB, "apron", 6
00300D0909DC, "tshirt", 6
00300D0909DD, "trousers", 6
00300D0909DE, "tshirt", 1
00300D0909DF, "trousers", 1
00300D0909E0, "trousers", 6
documents:
0,2015-08-01,"in"
1,2015-08-02,"in"
2,2015-08-03,"out"
3,2015-08-04,"in"
4,2015-08-05,"out"
5,2015-08-06,"in"
doc_pcs:
0, 1, 00300D0909DA
0, 2, 00300D0909DE
1, 1, 00300D0909DF
1, 2, 00300D0909DD
2, 1, 00300D0909DE
3, 1, 00300D0909DB
4, 1, 00300D0909DF
5, 1, 00300D0909DC
5, 2, 00300D0909E0
query results for given doc_date=2015-08-04 including fields rfid_no and item_name should be:
00300D0909DA, "tshirt"
00300D0909DB, "apron"
00300D0909DD, "trousers"
00300D0909DF, "trousers"
EDIT:
I've managed to make following query but it executes endlessly hence I believe something is wrong:
SELECT DISTINCT c.rfid_no
FROM itemstock c LEFT JOIN doc_pcs d
ON c.rfid_no=d.rfid_no
WHERE c.status=6
OR
(d.id IN
(SELECT dk.id
FROM documents dk
WHERE DATE(dk.doc_date)>='2015-08-04'
AND dk.doc_type="out")
AND
d.id NOT IN
(SELECT dk.id
FROM documents dk
WHERE DATE(dk.doc_date)>='2015-08-04'
AND dk.doc_type="in"))
It seems like you could either go from today's values and work backwards or you could start from 'day 0' and work forwards. By storing the stock count as of 'today' you are denormalizing the data so I'd opt for the second approach (and maybe you could save yourself a table).
Assume that as of 'day 0' all stock is empty. To get the stock count as of a given date you would (p-query):
select sum((select count inbound where date <= inventory_date) - (select count outbound where date <= inventory_date))

mysql query if there is no record or record.xx != xx trouble with query

I've 3 MYSQL tables.
"villa" table has records for properties.
"villa_type" table has records for property types.
"villa_price" table for daily prices with dates, price, property id and availability columns.
I receive all data with SQL, left join it with villa_type table to receive some additional info about property which works ok, and left join the table with the 3rd table to ask if the property is booked on that date or available.
Query is like this.
SELECT villa.*, villa_type.tip as villatypename
FROM villa
LEFT JOIN villa_type on villa_type.id = villa.type
LEFT JOIN villa_price on villa_price.villaid = villa.id
WHERE villa.lang = 1
AND villa.area = 1980
AND villa.status = 1
AND villa.status2 = 1
-- Problem starts here..............
AND (villa_price.vdate = '2015-01-13' OR villa_price.vdate is null)
AND (
((villa_price.condition != 2) AND (villa_price.condition != 3))
OR (villa_price.condition is null)
)
The problem is: if the villa has a record for 2015-01-13 and its condition is 2 or 3, i correctly don't get that villa listed (which is what i'm trying to do), but if that villa has a record for that same date and the condition is 1, it still doesn't get listed (while it should).
I simply want to check villa_price table to see that villa has a condition of 2 or 3 for the given date and eliminate that villa from result.
Any help or guide will be appreciated. Thanks alot

Codeigniter mysql query joining 2 values across 3 tables

Although I can get the joins to work, it doesn't return the lowest deal possible. The result should be lowest handset price possible with the lowest monthly cost. Currently I can get the lowest handset price, but not with the lowest monthly cost and similarly lowest monthly cost but not with the lowest priced handset.
I have 3 tables DEALS, TARIFFS and HANDSETS. When a customer comes to a manufacturer product listing page, it loads all products related to that manufacturer (in most instances around 40 products). What I would like is it to load the cheapest deal available for each product. Tariff table has over 4000 records. Handset table has over 3000 records. Deals table has over 1 million records.
There is a representation of the the 3 tables below with relevant columns:
Handset Table
==================================
id | manufacturer_id
-----------------------------------
100 1
Deals Table
==================================================
tariff_id | handset_id | handset_cost
--------------------------------------------------
15 100 44.99
20 100 114.99
Tariffs Table
==============================
id | monthly_cost
------------------------------
15 12.50
20 7.50
This is the query
$this->db->select('h.name, t.monthly_cost, d.handset_cost');
$this->db->from('aff_deals d');
$this->db->join('aff_tariffs t', 't.id = d.tariff_id', 'inner');
$this->db->join('aff_handsets h', 'h.id = d.handset_id', 'inner');
if (isset($manuid)) {
$this->db->where('h.manufacturer_id', $manuid);
}
$this->db->order_by('d.handset_cost ASC');
$this->db->order_by('t.monthly_cost ASC');
$this->db->group_by('h.name');
$query = $this->db->get();
$result = $query->result();
Unfortunately this returns handset cost of £114.99 and monthly cost of £7.50, when I need £44.99 and £12.50. The example above is a simple snap shot. I have tried MIN(d.handset_cost), sub-queries but cannot get the desired results. Somehow I need to be able to get the lowest price handsets, even if it's 0.00 (FREE), with its equivalent monthly cost. Any help would be most welcome.
According to your query you are misusing Mysql's GROUP BY extension without having any aggregate function this will lead your query to return indeterminate results for columns which are absent in group by taking your query as an example,columns t.monthly_cost, d.handset_cost values are indeterminate if you are specific to pick minimum row from deals table per handset then you can use below query
SELECT h.name,
t.monthly_cost,
d.handset_cost
FROM aff_deals d
INNER JOIN (SELECT handset_id,MIN(handset_cost) handset_cost
FROM aff_deals
GROUP BY handset_id) dd
ON d.handset_id = dd.handset_id AND d.handset_cost = dd.handset_cost
INNER JOIN aff_tariffs t ON t.id = d.tariff_id
INNER JOIN aff_handsets h ON h.id = d.handset_id
WHERE h.manufacturer_id =1
ORDER BY d.handset_cost ASC,t.monthly_cost ASC
See Demo
For active record query it will difficult to replicate above (subselect) query bu you can directly run this query through $this->db->query('your query')

Compare rows in same table in mysql

I have a mysql table like below
id trader price
111 abc 5
222 xyz 5.20
333 abc 5.70
444 xyz 5
555 abc 5.20
I need to compare row 1's price with row 2's price and accroding to the given sample row 2's price is less than row 1's price which means that trader xyz increased the price once, and in the same way when we compare row 2's price with row 3's price where row 3's price is higher than row 2's price which means trader abc also increased the price once. So in this manner I need to compare the entire table and find how many times each trader increased or decreased price...
I dont have any idea to do this, can someone please help me on this
You can perform a "self-join" (joining the table to itself) to perform queries. The tricky part here is knowing the order in which rows were inserted into the table, and only comparing rows that are sequentially (temporally) adjacent. I'm assuming you have some sort of TIMESTAMP column that will tell you which price changes came after the prior ones. If not, then perhaps "ID" can inform you of that (the greater ID row being inserted after the lesser ID).
Calling your table 'TAB', using 'TRADER' to provide the join, and using 'ID' to provide the Order, the query would require a three-way self-join such as follows:
SELECT a.trader
, SUM(IF(a.price > b.price, 1, 0)) nbr_incr
, SUM(IF(a.price < b.price, 1, 0)) nbr_decr
, SUM(IF(a.price = b.price, 1, 0)) nbr_same
FROM tab a
JOIN tab b
ON a.trader = b.trader AND a.id > b.id
LEFT OUTER JOIN tab c
ON a.trader = c.trader AND a.id > c.id AND b.id < c.id
WHERE c.id IS NULL
GROUP BY a.trader
The above query joins the table to itself twice so that each tab represents the following:
tab a : The more recent row for comparison
tab b : The immediately prior row to compare against
tab c : A row between a & b timewise (should not exist)
We perform a LEFT OUTER JOIN to 'tab c' because we do not actually want that row to exist. In the where clause, we filter our results only to the results where a 'tab c' row does not exist.
Finally, the query performs a 'GROUP BY' on the trader, and SUM()s the Increments and Decrements by comparing the price from the 'a' and 'b' rows.
This was a fun challenge. Hope this helps!
john...
You will need to setup a separate table if you want to track a history of price changes. Also, when you refer to columns, it sounds like you mean rows.

Is it possible to construct a single query to match two tables enforcing a 1:1 relationship between them?

For those interested in the reasoning behind this question: I have an e-commerce site that works fine, but has no gift certificate capabilities. Adding monetary GCs should be pretty simple, but I'd also like to allow the gifting of specific products (sounds odd but is relevant to my industry). So I plan to create a new table to house gift certificates that are linked to a specific user and product, and I need an efficient way to evaluate that table on the cart and checkout pages.
Imagine tables exist that look similar to the following:
CartContents
CartID Integer (Unique sequential row identifier)
UserID Integer
ProductID Integer
Quantity Integer
Gifts
GiftID Integer (Unique sequential row identifier)
ProductID Integer
UserID Integer
Quantity Integer
This is an overly simplified layout, but demonstrates the idea. The first table lists items in the user's cart; one record per product (though real products will have additional details that may vary). The product table has further attributes on products but I don't list it here for simplicity. The second table is a set of gift certificates, each for a specific product, that have been presented to this user ID.
Table data may look like the following:
CartContents
CartID UserID ProductID Quantity
1 1 1 1
2 1 2 2
3 1 1 2
4 2 3 1
Gifts
ProductID UserID Quantity
1 1 1
2 1 1
3 3 1
Is it possible to construct a single query that provides one row per cart item and links the above two tables taking into account that each gift may only link to each cart item once? Or does this need to be handled in a script?
In other words, because user 1 has product 1 in their cart twice, and they have only been promised one free product 1, the query should return a matching Gifts record for cartID 1, but not cartID 3. The query, pulling for user ID 1, would return:
CartID ProductID Quantity unpaidQuantity
1 1 1 0
2 2 2 1
3 1 2 2
Or
CartID ProductID Quantity unpaidQuantity
1 1 1 1
2 2 2 1
3 1 2 1
I realize that the fact that there is more than one 'right' answer to this question raises a red flag. In reality it doesn't matter which cart record each GC is applied to, as the end result (the price) will work out the same. I'm perfectly happy to say the 'first' (lowest cartID) is the one that should be linked.
My assumption is that the database will be far more efficient at this than any script I could write; I'd even be willing to bet there's some crazy type of join I've never heard of specifically designed for it. I am also assuming that any such ColdFusion script may be somewhat complicated and thus take a fair amount of development and testing time while a single query may be relatively simple (though apparently beyond my limited SQL capabilities). If I'm incorrect in this I'd appreciate any thoughts on that as well.
My setup, if it matters:
MySQL 5.0
ColdFusion 9
Windows 2000 AS
Edit:
It sounds like the quantity column is really going to cause issues, so let's continue assuming that quantity does not exist on the Gifts table. It still must exist on cartContents, however.
I thought of another way of doing this that just requires and additional group by and join. However, it requires a unique id on CartContents. I'm not sure i this is what CartId is supposed to be. However, it seems that a user could have more than one cart, so I assume not.
The idea is to identify the first record for a given product in each cart. Then, use this information when joining in the gifts.
select CartID, UserID, ProductID, Quantity, FirstCCId
from CartContents cc join
(select CartID, UserID, ProductID, min(CartContentsId) as FirstCCId
from CartContents cc
group by CartID, UserID, ProductID
) ccmin
on cc.CartId = ccmin.CartId and cc.UserId = ccmin.UserId and
cc.ProductId = ccmin.ProductId left outer join
Gifts g
on cc.ProductID= g.ProductId and cc.UserID = g.userId and
cc.CartContentsId = ccmin.FirstCCId
This works when the gift is applied to only one product line row. If the quantity for the gift is actually larger than the quantity on any given line, this query still only puts it on one line.
Does this work?
select c.cartid, c.productid, c.quantity, c.quantity -
case
when (select sum(c2.quantity) from CartContents c2
where c.userid = c2.userid
and c.productid = c2.productid
and c.cartid < c2.cartid) <
(select g.quantity from gifts g
where c.userid = g.userid
and c.productid = g.productid) then
(select g.quantity from gifts g
where c.userid = g.userid
and c.productid = g.productid) -
(select sum(c2.quantity) from CartContents c2
where c.userid = c2.userid
and c.productid = c2.productid
and c.cartid < c2.cartid)
else 0
end UnpaidQuantity
from CartContents c
where userid = 1