Left Join in MySQL wrong output table - mysql

I did a left join to obtain the data that I wanted. However, the output table has 4 columns, where the first 3 have the right data but the last column doesn't match with the other 3. So, to be specific in one table I have the names and in the other I have dates, usernames, and "comments". What makes it difficult is that I need to have only the most recent dates in a table that is changed daily. This is my query:
SELECT CONCAT(ur.USR_FIRSTNAME, ur.USR_LASTNAME) AS 'NAME',
aav.USR AS 'USERNAME',
IF(aav.TIME_STAMP > NOW() + INTERVAL -4 HOUR,
aav.AVAILABLE_DATE,MAX(aav.AVAILABLE_DATE)) AS 'DATE_AVAILABLE',
aav.COMMENTS AS 'COMMENTS'
FROM wi_workflow.USERS ur LEFT JOIN fii_tables.available_log aav ON ur.USR_USERNAME = aav.USR and ur.USR_UID = aav.USR_UID
LEFT JOIN fii_tables.available_log aav2 ON(aav.COMMENT = aav2.COMMENT)
WHERE ur.USR_POSITION = 'EDE'
AND ur.USR_STATUS = 'ACTIVE'
AND aav.TIME_STAMP > NOW() + INTERVAL - 45 DAY
GROUP BY 1;
Can somebody give me hint on how to have the last column matching the other 2?
Thanks.

I solved my problem this is the query that I used:
SELECT aev.ede_name,
aav.available_date,
aav.COMMENT AS 'COMMENT',
IF(aav.AVAILABLE_DATE < NOW() + INTERVAL 5 DAY, "AVAILABLE THIS WEEK",'NOT AVAILABLE') AS 'AVAILABILITY'
FROM fii_tables.available_log aav
INNER JOIN (SELECT aav.`USR` AS usr,max(aav.TIME_STAMP) AS `TIME_STAMP` from fii_tables.available_log aav group by aav.USR;) lts ON ( lts.usr = aav.usr AND lts.time_stamp = aav.time_stamp )
LEFT JOIN wf_workflow.actual_ede_v aev ON(aav.usr = aev.usr_username)
where aev.ede_name is not null
GROUP BY aav.usr, aav.time_stamp;

Related

Calculating payment breakages

I'm currently working on a report to highlight payment breakages, this is based on a customer paying in June, but then failing to pay in July.
I've currently got it set up to do an except query, to check one month and compare it to the next. Similar to below(syntax my not be correct as I have had to edit certain data).
DECLARE #StartDatePaid AS DATETIME
DECLARE #EndDatePaid AS DATETIME
DECLARE #StartDateMissed AS DATETIME
DECLARE #EndDateMissed AS DATETIME
SET #StartDatePaid = '01-Oct-2013'
SET #EndDatePaid = '31-Oct-2013'
SET #StartDateMissed = '01-Nov-2013'
SET #EndDateMissed = '05-Dec-2013'
SELECT d.StoreNo
, d.CustNo
FROM (
--Paid Range
SELECT c.CustNo, m.StoreNo
FROM dbo.tblCont AS c INNER JOIN
dbo.tblContDep AS cd ON c.ContractNo = cd.ContractNo INNER JOIN
dbo.tblCust AS m ON c.CustNo = m.CustNo INNER JOIN
dbo.tblTrans AS mx ON m.CustNo = mx.CustNo AND cd.AgendaCode = mx.AgendaCode INNER JOIN
dbo.tblCalender AS cl ON mx.DateEvent = cl.Date
WHERE (cd.Payment > 0) AND (m.Closed <> 'Y') AND (cd.AgendaCode <> 'OPCLIPMT')
AND mx.DateEvent BETWEEN #StartDatePaid AND #EndDatePaid
GROUP BY c.CustNo, m.StoreNo, mx.DateEvent
EXCEPT
--Missed Range
SELECT c.CustNo, m.StoreNo
FROM dbo.tblCont AS c INNER JOIN
dbo.tblContDep AS cd ON c.ContractNo = cd.ContractNo INNER JOIN
dbo.tblCust AS m ON c.CustNo = m.CustNo INNER JOIN
dbo.tblTrans AS mx ON m.CustNo = mx.CustNo AND cd.AgendaCode = mx.AgendaCode INNER JOIN
dtLookups.dbo.tblCalender AS cl ON mx.DateEvent = cl.Date
WHERE (cd.Payment > 0) AND (m.Closed <> 'Y') AND (cd.AgendaCode <> 'OPCLIPMT') AND (mx.DateEvent BETWEEN #StartDateMissed AND #EndDateMissed )
GROUP BY c.CustNo, m.StoreNo, mx.DateEvent
) AS d
WHERE d.StoreNo IN (72, 114, 121, 139, 185, 241, 266)
GROUP BY
d.StoreNo, d.CustNo
I will be switching it over to be based on calendar months instead of date ranges, my question is how am I best generating several months of breakages at once. To get a month on Month comparison at once, as it is I can only get it to create one months breakages based on supplied data.
Example of desired output
Month| breakges
June | 201
July | 189
Aug | 250
Open to suggestions on best practice also or ways to improve.
I admit I don't understand your query. Assuming your breakage is the first occurrence of missing payment, not the subsequent ones. You can produce the desired output like this:
-- prepare your source data
with cte1 as
(
select
user_id,
date, -- representing month by the first day
missed -- bool flag if payment was missed in that month
from ...
)
-- add a sequence number to the source data ordered by date
with cte2 as
(
select *,
row_number() over(partition by user_id order by date) rn
from cte1
)
-- select those records where payment was missed but the previous was ok
,cte3 as
(
select user_id, date from cte2 a
where a.missed = 1
and exists (
select * from cte2 b
where b.missed = 0
and b.uid = a.uid
and b.rn = a.rn -1
)
)
select date, count(*) as breakage from cte3 group by date

MYSQL Query with Multiple Selects from Same Table

Getting an error
Operand should contain 1 column(s)
PK is ID
The table just dumps data in to the table
need to get the earliest date qty and the latest date qty and display on the same column
Any help appreciated
SELECT ebx_r_history.ItemNumber,
(SELECT r.QuantitySold as newqty, r.lastupdate as lu
FROM ebx_r_history r
WHERE ebx_r_history.ItemNumber = r.ItemNumber AND ebx_r_history.SKU = r.SKU
ORDER BY r.LastUpdate ASC
LIMIT 1),
(SELECT r.QuantitySold as newqty, r.lastupdate as lu
FROM ebx_r_history r
WHERE ebx_r_history.ItemNumber = r.ItemNumber AND ebx_r_history.SKU = r.SKU
ORDER BY r.LastUpdate DESC
LIMIT 1)
FROM
ebx_r_history
GROUP BY ebx_r_history.ItemNumber,
ebx_r_history.SKU
ORDER BY ebx_r_history.LastUpdate
This version may offer a simplified and faster alternative for you. The inner query for "AllItems" does both a min and max of the last update on a per-item number/sku basis, although I believe they would be one-in-the-same record.
So now, join that results back to the history data by item/sku and only those that match either the min or max date. If a true date/time, there would expect to only be one anyhow, vs just a date-only. So, since there would be 2 possible records (one for the min, one for the max), I am applying a MAX( IIF( )) for each respective matching the minimum and maximum dates respectively and must retain the group by clause.
Note, if you are dealing with date-only entries, or possibilities of the exact same item/sku and lastupdate are the same to the second, then you would need an approach more towards limit 1 per ascending/descending basis.
SELECT
AllItems.ItemNumber,
AllItems.SKU,
AllItems.MinUpdate,
MAX( IIF( rh.lastupdate = AllItems.MinUpdate, rh.Quantity.Sold, 0 )) as QtyAtMinDate,
AllItems.MaxUpdate,
MAX( IIF( rh.lastupdate = AllItems.MaxUpdate, rh.Quantity.Sold, 0 )) as QtyAtMaxDate
from
( SELECT
r.ItemNumber,
r.SKU,
MIN( r.lastupdate ) as MinUpdate,
MAX( r.lastupdate ) as MaxUpdate
FROM
ebx_r_history r
group by
r.ItemNumber,
r.SKU ) AllItems
JOIN ebx_r_history rh
ON AllItems.ItemNumber = rh.ItemNumber
AND AllItems.SKU = rh.SKU
AND ( rh.lastUpdate = AllItems.MinUpdate
OR rh.lastUpdate = AllItems.MaxUpdate )
group by
AllItems.ItemNumber,
AllItems.SKU
Per another answer where you were only looking to IGNORE items within the most recent 14 days, you can just add a WHERE clause to the inner query similar via
WHERE r.LastUpdate >= CURDATE() - INTERVAL 14 DAY
If your history table has an auto-incrementing ID column, AND the respective transactions have the lastUpdate sequentially stamped, such as when they are added and not modified by any other operation, then you could just apply similar but MIN/MAX of the ID column, then join back TWICE on the ID and just each row ONCE such as...
SELECT
AllItems.ItemNumber,
AllItems.SKU,
rhMin.LastUpdate as MinUpdate,
rhMin.QuantitySold as MinSold,
rhMax.LastUpdate as MaxUpdate,
rhMax.QuantitySold as MaxSold
from
( SELECT
r.ItemNumber,
r.SKU,
MIN( r.AutoIncrementColumn ) as MinAutoID,
MAX( r.AutoIncrementColumn ) as MaxAutoID
FROM
ebx_r_history r
group by
r.ItemNumber,
r.SKU ) AllItems
JOIN ebx_r_history rhMin
ON AllItems.MinAutoID = rhMin.AutoIncrementColumn
JOIN ebx_r_history rhMax
ON AllItems.MaxAutoID = rhMax.AutoIncrementColumn
order by
rhMax.LastUpdated
Try something like this:
SELECT r1.ItemNumber,
(
SELECT r.QuantitySold
FROM ebx_r_history r
WHERE r1.ItemNumber = r.ItemNumber
AND r1.SKU = r.SKU
ORDER BY r.LastUpdate ASC LIMIT 1
) AS earliestDateQty,
(
SELECT r.QuantitySold
FROM ebx_r_history r
WHERE r1.ItemNumber = r.ItemNumber
AND r1.SKU = r.SKU
ORDER BY r.LastUpdate DESC LIMIT 1
) AS latestDateQty
FROM ebx_r_history r1
GROUP BY r1.ItemNumber,r1.SKU
ORDER BY 3
You had a couple of errors. you were getting two columns inside the inner selects, and you had a couple of places where you might get the error for ambiguous column name.
sqlFiddle here
SELECT T1.ItemNumber,
T1.SKU,
T1.Old_QuantitySold,
T1.Old_LastUpdate,
T2.New_QuantitySold,
T2.New_LastUpdate
FROM
(SELECT itemNumber,SKU,QuantitySold as Old_QuantitySold,LastUpdate as Old_LastUpdate
FROM ebx_r_history r
WHERE NOT EXISTS (SELECT 1 FROM ebx_r_history e
WHERE e.itemNumber = r.itemNumber AND e.SKU = r.SKU
AND e.LastUpdate < r.LastUpdate)
)T1
LEFT JOIN
(SELECT itemNumber,SKU,QuantitySold as New_QuantitySold,LastUpdate as New_LastUpdate
FROM ebx_r_history r
WHERE NOT EXISTS (SELECT 1 FROM ebx_r_history e
WHERE e.itemNumber = r.itemNumber AND e.SKU = r.SKU
AND e.LastUpdate > r.LastUpdate)
)T2 ON (T2.itemNumber = T1.itemNumber AND T2.SKU = T1.SKU)
WHERE T1.Old_LastUpdate >= CURDATE() - INTERVAL 14 DAY
AND T2.New_LastUpdate >= CURDATE() - INTERVAL 14 DAY
ORDER BY T2.New_LastUpdate;
you can do left join or inner join it's up to you, since T1 will always get earliest records and T2 will always get latest records for the ItemNumber,SKU grouping.
UPDATED TO IGNORE DATA OLDER THAN 14 DAYS
SELECT T1.ItemNumber,
T1.SKU,
T1.Old_QuantitySold,
T1.Old_LastUpdate,
T2.New_QuantitySold,
T2.New_LastUpdate
FROM
(SELECT itemNumber,SKU,QuantitySold as Old_QuantitySold,LastUpdate as Old_LastUpdate
FROM ebx_r_history r
WHERE LastUpdate >= CURDATE() - INTERVAL 14 DAY
AND NOT EXISTS (SELECT 1 FROM ebx_r_history e
WHERE e.itemNumber = r.itemNumber AND e.SKU = r.SKU
AND e.LastUpdate >= CURDATE() - INTERVAL 14 DAY
AND e.LastUpdate < r.LastUpdate)
)T1
LEFT JOIN
(SELECT itemNumber,SKU,QuantitySold as New_QuantitySold,LastUpdate as New_LastUpdate
FROM ebx_r_history r
WHERE LastUpdate >= CURDATE() - INTERVAL 14 DAY
AND NOT EXISTS (SELECT 1 FROM ebx_r_history e
WHERE e.itemNumber = r.itemNumber AND e.SKU = r.SKU
AND e.LastUpdate >= CURDATE() - INTERVAL 14 DAY
AND e.LastUpdate > r.LastUpdate)
)T2 ON (T2.itemNumber = T1.itemNumber AND T2.SKU = T1.SKU)
ORDER BY T2.New_LastUpdate;
ignore data older than 14 days sqlFiddle here
If you want to use exact time (14 days ago), you can replace occurences of CURDATE() with NOW()

Select value in mysql but check another database at clause WHERE

I'm trying to select values from a database, but I need to check another value in another database .
I created this code, but only get 1 result and I don't know why:
SELECT `id` FROM `mc_region`
WHERE `is_subregion` = 'false'
AND lastseen < CURDATE() - INTERVAL 20 DAY
AND (SELECT id_region FROM mc_region_flags
WHERE flag <> 'expire'
AND id_region = mc_region.id
)
LIMIT 0, 30
What I've made wrong?
#Edit
I think I know why this code is not working. At database mc_region_flags not all records from the primary database has flag.
I would like to do the following:
1º Select all records on the first database, where is not subregion and lastseen is more than 20 day
2º Check if any result on the 1st database has flag 'expire', if yes, they are not included in the result.
I cant do this in 1 only SQL Code?
#Edit2
I created this code that simulate FULL JOIN but seems that WHERE is not work
SELECT *
FROM mc_region AS r RIGHT OUTER JOIN
mc_region_flags AS f ON r.id = f.id_region
UNION ALL
SELECT * from
mc_region AS r LEFT OUTER JOIN
mc_region_flags AS f
ON r.id = f.id_region
WHERE r.is_subregion = 'false'
AND f.flag = 'exipre'
AND r.lastseen < CURDATE() - INTERVAL 20 DAY
Problems WHERE not work
f.flag is not 'expire'
f.lastseen is not > 20 days
UPDATED
SELECT *
FROM `mc_region` AS r LEFT JOIN
`mc_region_flags` AS f ON r.`id` = f.`id_region`
WHERE r.`is_subregion` = 'false' AND
r.`lastseen` < CURDATE() - INTERVAL 20 DAY AND
COALESCE(f.`flag`, '-') <> 'expire'
LIMIT 0, 30;
Before the inner nested select add :
id in (select...)

mysql find date where no row exists for previous day

I need to select how many days since there is a break in my data. It's easier to show:
Table format:
id (autoincrement), user_id (int), start (datetime), end (datetime)
Example data (times left out as only need days):
1, 5, 2011-12-18, 2011-12-18
2, 5, 2011-12-17, 2011-12-17
3, 5, 2011-12-16, 2011-12-16
4, 5, 2011-12-13, 2011-12-13
As you can see there would be a break between 2011-12-13 and 2011-12-16. Now, I need to be able say:
Using the date 2011-12-18, how many days are there until a break:
2011-12-18: Lowest sequential date = 2011-12-16: Total consecutive days: 3
Probably: DATE_DIFF(2011-12-18, 2011-12-16)
So my problem is, how can I select that 2011-12-16 is the lowest sequential date? Remembering that data applies for particular user_id's.
It's kinda like the example here: http://www.artfulsoftware.com/infotree/queries.php#72 but in the reverse.
I'd like this done in SQL only, no php code
Thanks
SELECT qmin.start, qmax.end, DATE_DIFF( qmax.end, qmin.start ) FROM table AS qmin
LEFT JOIN (
SELECT end FROM table AS t1
LEFT JOIN table AS t2 ON
t2.start > t1.end AND
t2.start < DATE_ADD( t1.end, 1 DAY )
WHERE t1.end >= '2011-12-18' AND t2.start IS NULL
ORDER BY end ASC LIMIT 1
) AS qmax
LEFT JOIN table AS t2 ON
t2.end < qmin.start AND
t2.end > DATE_DIFF( qmin.start, 1 DAY )
WHERE qmin.start <= '2011-12-18' AND t2.start IS NULL
ORDER BY end DESC LIMIT 1
This should work - left joins selects one date which can be in sequence, so max can be fineded out if you take the nearest record without sequential record ( t2.anyfield is null ) , same thing we do with minimal date.
If you can calculate days between in script - do it using unions ( eg 1. row - minimal, 2. row maximal )
Check this,
SELECT DATEDIFF((SELECT MAX(`start`) FROM testtbl WHERE `user_id`=1),
(select a.`start` from testtbl as a
left outer join testtbl as b on a.user_id = b.user_id
AND a.`start` = b.`start` + INTERVAL 1 DAY
where a.user_id=1 AND b.`start` is null
ORDER BY a.`start` desc LIMIT 1))
DATEDIFF() show difference of the Two days, if you want to number of consecutive days add one for that result.
If it's not a beauty contents then you may try something like:
select t.start, t2.start, datediff(t2.start, t.start) + 1 as consecutive_days
from tab t
join tab t2 on t2.start = (select min(start) from (
select c1.*, case when c2.id is null then 1 else 0 end as gap
from tab c1
left join tab c2 on c1.start = adddate(c2.start, -1)
) t4 where t4.start <= t.start and t4.start >= (select max(start) from (
select c1.*, case when c2.id is null then 1 else 0 end as gap
from tab c1
left join tab c2 on c1.start = adddate(c2.start, -1)
) t3 where t3.start <= t.start and t3.gap = 1))
where t.start = '2011-12-18'
Result should be:
start start consecutive_days
2011-12-18 2011-12-16 3

mysql day report query

The query below gives me a report of items that are out for an equipment rental company. this is a super complicated query that takes almost 20 seconds to run. This is obviously not the correct way to get the data that I'm looking for. I build this query from PHP and add in the start date of 02-01-2011 and the end date of 03-01-2011, the product code (p_code = 1) and product pool (i_pool =1). Those 4 pieces of information are passed to a PHP function and injected into the following sql to return the report I need for a calendar control displaying how many items are out. My question is: Is there any way to simplify or do this better, or run more efficiently, using better joins or a better way to display the individual days.
SELECT DISTINCT reportdays.reportday, count(*)
FROM
(SELECT '2011-02-01' + INTERVAL a + b DAY reportday
FROM
(SELECT 0 a UNION SELECT 1 a UNION SELECT 2 UNION SELECT 3
UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7
UNION SELECT 8 UNION SELECT 9 ) d,
(SELECT 0 b UNION SELECT 10 UNION SELECT 20
UNION SELECT 30 UNION SELECT 40) m
WHERE '2011-02-01' + INTERVAL a + b DAY < '2011-03-01'
ORDER BY a + b) as reportdays
JOIN rental_inv as line
ON DATE(FROM_UNIXTIME(line.ri_delivery_dte)) <= reportdays.reportday
AND DATE(FROM_UNIXTIME(line.ri_pickup_dte)) >= reportdays.reportday
LEFT OUTER JOIN rental_in as rent on line.ri_num = rent.ri_num
LEFT OUTER JOIN rental_cancels cancelled on rent.ri_num = cancelled.ri_num
LEFT OUTER JOIN inv inventory on line.i_num = inventory.i_num
LEFT OUTER JOIN product ON inventory.p_code = product.p_code
WHERE rent.ri_extend = 0 -- disregard extended rentals
AND cancelled.ri_num is null -- disregard cancelled rentals
AND inventory.p_code = 1
AND inventory.i_pool = 1
GROUP BY reportdays.reportday
If there is any other information needed, let me know and I'll post it.
You can use:
SELECT DATE(ri_delivery) as day,
count(*) as itemsout,
FROM rental_inv
GROUP BY day;
I'm not sure if you need this or a different thing.
SELECT dates.day, count (*)
FROM rental_inv line
INNER JOIN (SELECT DATE(ri_delivery_dte) as day FROM rental_inv
WHERE ri_delivery_dte >= '2011/02/01'
AND ri_delivery_dte <= '2011/02/28'
GROUP BY day
UNION
SELECT DATE(ri_pickup_dte) as day FROM rental_inv
WHERE ri_pickup_dte >= '2011/02/01'
AND ri_pickup_dte <= '2011/02/28'
GROUP BY day) dates
ON line.ri_delivery_dte <= dates.day and line.ri_pickup_dte >= dates.day
LEFT JOIN rental_cancels canc on line.ri_num = canc.ri_num
LEFT JOIN rental_in rent on line.ri_num = rent.ri_num
WHERE canc.ri_num is null
AND rent.ri_extend = 0
GROUP BY dates.day
to find all days:
SELECT DATE(IFNULL(ri_delivery,ri_pickup)) AS date FROM rental_inv AS dateindex WHERE [YEAR-MONTH-1] <= ri_delivery <= LAST_DAY([YEAR-MONTH-1]) OR [YEAR-MONTH-1] <= ri_pickup <= LAST_DAY([YEAR-MONTH-1]) GROUP BY date HAVING NOT ISNULL(date)
to find items out
SELECT COUNT(id) FROM rental_inv WHERE ri_pickup = [DATE];
to find items in
SELECT COUNT(id) FROM rental_inv WHERE ri_delivery = [DATE];
to find balance
SELECT COUNT(out.id) - COUNT(in.id) FROM rental_inv AS out INNER JOIN rental_inv AS in
ON DATE(out.ri_pickup) = DATE(in.ri_delivery) WHERE out.ri_pickup = [DATE] OR in.ri_delivery = [DATE]
You probably can join up everything but since its procedure its more clear;
I am not sure if this would be the exact answer to your problem but I would do something like this I guess. (I didn't use any SQL editor so u need to check syntax I guess)
SELECT
reportdays.d3 as d,
( COALESCE(outgoing.c1,0) - COALESCE(incoming.c2,0) ) as c
FROM
-- get report dates
(
SELECT DATE(FROM_UNIXTIME(COALESCE(l3.ri_delivery_dte, l3.ri_pickup_dte)) d3
FROM rental_inv l3
WHERE
(l3.ri_delivery_dte >= UNIX_TIMESTAMP('2011-02-01')
AND l3.ri_delivery_dte < UNIX_TIMESTAMP('2011-03-01'))
OR (l3.ri_pickup_dte >= UNIX_TIMESTAMP('2011-02-01')
AND l3.ri_pickup_dte < UNIX_TIMESTAMP('2011-03-01'))
GROUP BY d3
) as reportdays
-- get outgoing
LEFT JOIN (
SELECT DATE(FROM_UNIXTIME(l1.ri_delivery_dte)) as d1, count(*) as c1
FROM rental_inv l1
LEFT JOIN rental_cancels canc1 on l.ri_num = canc1.ri_num
LEFT JOIN rental_in rent1 on l.ri_num = rent1.ri_num
WHERE
l1.ri_delivery_dte >= UNIX_TIMESTAMP('2011-02-01')
AND l1.ri_delivery_dte < UNIX_TIMESTAMP('2011-03-01')
AND canc1.ri_num is null
AND rent1.ri_extend = 0
GROUP BY d1
) as outgoing ON reportdays.d3 = outgoing.d1
-- get incoming
LEFT JOIN (
SELECT DATE(FROM_UNIXTIME(l2.ri_pickup_dte)) as d2, count(*) as c2
FROM rental_inv l2
LEFT JOIN rental_cancels canc2 on l2.ri_num = canc2.ri_num
LEFT JOIN rental_in rent2 on l2.ri_num = rent2.ri_num
WHERE
l2.ri_pickup_dte >= UNIX_TIMESTAMP('2011-02-01')
AND l2.ri_pickup_dte < UNIX_TIMESTAMP('2011-03-01')
AND canc2.ri_num is null
AND rent2.ri_extend = 0
GROUP BY d2
) as incoming ON reportdays.d3 = incoming.d2