mysql order with many criterias on two tables - mysql

I'm trying to sort a visitor list after some different criteria and got stuck, as I can't figure out, how to do this.
I have a queue of people who check in first, and out of that the list is generated. The client is marked as showedUp, if he comes to the door (after called with his number on the list). If someone comes late, he must be at the end of the list. Another thing is, the list starts everytime with a different number.
Day 1 -> List from 1 to 160
Day 2 -> List from 33 to 160, 1 to 32
Day 3 -> List from 65 to 160, 1 to 64
If someone comes late, meaning the number after him is already called, he should be added to the end of the list, like 1 to 160, 10 was late, as 20 was already called, it should be 1 to 160, 10. If there is another starting number it should be 33 to 160, 1 to 32, 10. The criteria here is: if a placeNr after your number is already called (showedUp), than you be at the end of the list.
Tables
clients (id, name, placeNr)
visits (id, pid, checkInTime, showedUp, showedUpTime)
Select
SELECT clients.id AS id, visits.id AS visitId, clients.placeNr AS placeNr, clients.name AS name
FROM clients, visits
WHERE clients.id = visits.pid AND visits.checkInTime >= '1447286401' AND visits.checkInTime <= '1447372799'
ORDER BY clients.placeNr < '1', if(visits.showedUpTime < visits.checkInTime, clients.placeNr, 1), ttc.placeNr
So how do I get the late showers at the end of my list?
Thank you very much in advance!

If I follow your logic, you need to specify whether or not someone is late. The following is the structure that you want for this type of query. I think I've captured the rules in your question:
select v.id, v.id AS visitId, c.placeNr, c.name,
(case when v.showedUpTime >
(select min(v.checkInTime)
from visits v2 join
clients c2
on v2.pid = c2.id
where date(v2.showedUpTime) = date(v.showedUpTime) and
c2.placeNr > c.placeNr
)
then 1 else 0 end) as IsLate
from clients c join
visits v
on c.id = v.pid
order by date(v.showedUpTime),
isLate,
c.placeNr;

Related

mySQL Sum Production_Needed Group BY Part_ID

Want to generate a result of Open orders where Production is needed. At issue is each part may have more than one open order. With the GROUP BY my code gives me only one order but does give me the total Production_Needed (which is also a negative in value for orders with enough inventory).
Does my SUM(...) as Production_Needed need to be in the WHERE ?
Thanks,
SELECT part.part_ID AS Part_Part_ID,
part.Inventory, part.part_number,
ord.part_id AS Order_Part_ID,
ord.order_type, ord.quantity_ordered, ord.quantity_shipped,
SUM(ord.quantity_ordered - ord.quantity_shipped - part.Inventory) AS Production_Needed
FROM production_orders ord
JOIN production_part part ON ord.part_ID = part.part_ID
WHERE ord.is_Active = True AND ord.order_type = 0
GROUP BY Order_Part_ID
ORDER BY part.part_number ASC
Data Production_Part part
Part_ID
Part_Inventory
Part_Number
1
12500
97-528
2
0
FC2569
3
1000
39367
Data Production_Orders Ord
Order_Part_ID
Order_Type
Quantity_Ordered
Quantity_Shipped
1
0
8000
0
2
0
1000
500
2
0
1000
0
3
1
10
0
Desired Result - Only Parts that need production
Part_ID
Quantity_Ordered
Quantity_Shipped
2
1000
500
2
1000
0
Untested: need a sampled data set and structure for testing:
This creates an inline view and totals the inventory order amounts then stubtracts it from the inventory to determine if there is a production needed to fulfil open orders. I'd have to use some additional analytical functions if we needed to do this on an order by order basis however; or join these results back into the orders...
--Show parts which lack inventory to fullfill outstanding open orders.
SELECT
P.Part_ID as Part_Part_ID
, P.Inventory
, P.Part_Number
, O.Part_ID as Order_Part_ID
, UnDel_Units-coalesce(P.Inventory,0) as Production_Needed --use coalesce incase no part record exists for some reason.
FROM Production_Part P
RIGHT JOIN ( --use right join just incase part record doesn't exist for some reason
SELECT part_ID, SUM(quantity_ordered-quantity_shipped) as UnDel_Units
FROM PRODUCTION_ORDERS
WHERE IS_ACTIVE=TRUE
and ORDER_TYPE=0
GROUP BY PART_ID) O --derived table "O" for orders showing sum ottal by part of units undelivered
on O.Part_ID=P.Part_ID
WHERE UnDel_Units > coalesce(P.Inventory,0)
-- If inventory is > undelivered units for the part, ignore as additional production isn't needed

My SUM with cases seems to be repeating twice

I have some camp management software that registers users for a camp.
I am trying to get how much a user owes on their account based on how much a camp costs and whether they are using the bus, and whether or not they sign up for the horse option. (These all cost extra).
I originally was grouping by registration_ids which a camper can have multiple of if they sign up for a camp. But when I put this in I get this:
https://imgur.com/i63Bnsu
This is my sql:
SELECT srbc_campers.camper_id,
/*Calculate how much the user owes*/
SUM(
srbc_camps.cost + (CASE WHEN srbc_registration.horse_opt = 1 THEN srbc_camps.horse_opt_cost
ELSE 0
END)
+
(CASE WHEN srbc_registration.busride = 'to' THEN 35
WHEN srbc_registration.busride = 'from' THEN 35
WHEN srbc_registration.busride = 'both' THEN 60
ELSE 0
END)
- IF(srbc_registration.discount IS NULL,0,srbc_registration.discount)
- IF(srbc_registration.scholarship_amt IS NULL,0,srbc_registration.scholarship_amt)
) AS owe
FROM (
srbc_registration INNER JOIN srbc_camps ON srbc_registration.camp_id=srbc_camps.camp_id)
INNER JOIN srbc_payments ON srbc_registration.registration_id = srbc_payments.registration_id)
INNER JOIN srbc_campers ON srbc_campers.camper_id=srbc_registration.camper_id)
WHERE NOT srbc_payments.payment_type='Store'
GROUP BY srbc_campers.camper_id
This seems to be affected by how many payments they have made in their account. It multiplies the amount they owe times how many individual payments were made toward that camp. I can't figure out how to stop this.
For instance in picture above^
We have camper_id #4 and they owe 678.
I expect camper_id #4 to owe 339. They have made 2 payments on their account in srbc_payments.
Haven't been using sql for that long, so any suggestions for a better way I am open too!
You are not selecting anything from srbc_payments, just checking for registration_id in srbc_payments. Or did you forget to subtract payments from srbc_payments? You can replace the inner join with:
where srbc_registration.registration_id in
(
select t1.registration_id from srbc_payments t1
where t1.registration_id = srbc_registration.registration_id
and t1.payment_type <> 'Store'
)
This is what I ended up getting to work how I wanted it too:
SELECT owedTble.registration_id,owe
FROM (SELECT registration_id,
SUM(
srbc_camps.cost + (CASE WHEN srbc_registration.horse_opt = 1 THEN srbc_camps.horse_opt_cost
ELSE 0
END)
+
(CASE WHEN srbc_registration.busride = 'to' THEN 35
WHEN srbc_registration.busride = 'from' THEN 35
WHEN srbc_registration.busride = 'both' THEN 60
ELSE 0
END)
- IF(srbc_registration.discount IS NULL,0,srbc_registration.discount)
- IF(srbc_registration.scholarship_amt IS NULL,0,srbc_registration.scholarship_amt)
) AS owe
FROM srbc_camps INNER JOIN srbc_registration ON srbc_camps.camp_id=srbc_registration.camp_id
GROUP BY srbc_registration.registration_id
) as owedTble
I kind of understand what I did here. I ended up trying different things from this answer: My SUM with cases seems to be repeating twice
Thanks for the helpful comments from #nick and #a_horse_with_no_name

MySQL take duplicate data and combine unique data

With my MySQL database, I want to take data from my temporary table and insert it into my main table, while removing any duplicate data but also taking into consideration the data I already have. This seems to require an update and/or an insert depending on what exists in "data_table" so I really have no idea how to write it or if it is even possible. If this isn't possible, I'd like to know how to accomplish this while not considering what is already in "data_table" which I would think is possible. Thank you for your help!
Existing data_table before running query:
data_table
+-----id-----+-----age-----+-----gender-----+-----color-----+
=============+==============+=================+================+
1 5 m pink,red,purple
data_table_temp
+-----id-----+-----age-----+-----gender-----+-----color-----+
=============+==============+=================+================+
1 5 m red
2 5 m blue
3 5 m red
4 5 m orange
5 6 m red
6 6 m green
7 6 m blue
After query:
data_table
+-----id-----+-----age-----+-----gender-----+-----color-----+
=============+==============+=================+================+
1 5 m pink,red,purple,blue,orange
2 6 m red,green,blue
Here is an approach to this problem which turned out to be harder than I expected.
The idea is to concat the colors that don't match and put them together. There is a bit of a problem assigning ids. Getting the "2" for the second row is a problem, so this just assigned the id sequentially:
select #id := #id + 1 as id,
coalesce(dt.age, dtt.age) as age,
coalesce(dt.gender, dtt.gender) as age,
concat_ws(dt.color,
group_concat(case when find_in_set(dtt.color, dt.color) > 0
then dtt.color
end)
)
from data_table_temp dtt left outer join
data_table dt join
on dt.age = dtt.age and
dt.gender = dtt.gender cross join
(select #id := 0) var
group by coalesce(dt.age, dtt.age), coalesce(dt.gender, dtt.gender);
MySQL doesn't have any string functions to (easily) split a delimited string (like data_table.color).
However, if you have all of the data in data_table_temp's format (one color per row), you can generate the desired results like this:
SELECT DISTINCT age, GROUP_CONCAT(DISTINCT color)
FROM table WHERE [condition]
GROUP BY age;
Optionally adding in gender, as necessary.
Apologies for the half-answer

mysql query if condition

Hi there i have two tables a2_deal(I havent mentioned entire table as its very big)
deviceID companyID stage serverTime
1 14 -1 1349449200
1 1 -1 1349445600
2 21 -1 1349449200
3 17 -1 1349447160
1 14 3 1344449200
1 14 2 1340449200
and another table called a2_comp
companyID name
1 Microsoft
14 DELL
15 APPLE
17 Google
I am trying to get the most recent stage of a company By using below query:
SELECT deal.companyID, companies.name as Company,
if(max(serverTime),stage,Null) as Stage
FROM `a2_deal` AS deal
LEFT JOIN `a2_comp` AS companies ON deal.companyID = companies.companyID
GROUP BY companyID
ORDER BY serverTime
in my query i am using if(max(serverTime),stage,Null) as Stage which means select the stage value related to most recent server time . ie it should give me -1 as the stage of companyID 14.... But for some reason i am not getting correct output..Please explain how my logic is wrong here... Thank You
You want the groupwise maximum:
SELECT a2_comp.*, a2_deal.*
FROM a2_deal NATURAL JOIN (
SELECT companyID, MAX(serverTime) AS serverTime
FROM a2_deal
GROUP BY companyID
) t JOIN a2_comp USING (companyID)
See it on sqlfiddle.
case is used for inline conditions in your query. Also, you may need to do
(case when max(serverTime) = serverTime then stage else null end) as Stage
I'm not totally sure that's valid, but you can try it out.
Try this
SELECT deal.companyID, deal.stage, comp.name
FROM a2_deal AS deal, a2_comp AS comp
WHERE deal.serverTime =
(SELECT MAX(deal2.serverTime)
FROM a2_deal AS deal2
WHERE deal2.companyID = deal.companyID)
AND comp.companyID = deal.companyID
GROUP BY deal.companyID
This might be a little confusing but the most interesting part is the sub query which selecting recent serverTime for each company. I have used theta style query and hence JOIN is not necessary.

MySQL - self join optimization

I have a table of phone events by HomeId. Each row has an EventId (on hook, off hook, ring, DTMF, etc), TimeStamp, Sequence (auto increment) and HomeId. Im working on a query to find specific types of occurrences(IE inbound or outbound calls) and duration.
I had planned on doing this using a multiple self-join on this table to pick out the sequences of events that usually indicate one type of occurrence or the other. EG inbound calls would be a period of inactivity followed by no DTMF, then ringing and caller id (possibly) then an off hook. I would find the next on-hook and thus have the duration.
My table is indexed by HomeId, EventId and Sequence and has ~60K records. When I do an 'explain' of my query it shows indexing and 75, 75, 1, 1, 748 for the row counts. Seems pretty doable. But when I run the query its taking more than 10 minutes (at which point the MySQL query browser times out).
Query for outbound calls:
select pe0.HomeId, pe1.Stamp, pe1.mSec, timediff( pe4.Stamp, pe0.Stamp ) from Phone_Events pe0
join Phone_Events pe1 on pe0.HomeId = pe1.HomeId and pe1.Sequence = pe0.Sequence - 1 and abs(timediff( pe0.Stamp, pe1.Stamp )) > 10
join Phone_Events pe2 on pe0.HomeId = pe2.HomeId and pe2.Sequence = pe0.Sequence + 1 and pe2.EventId = 22
join Phone_Events pe4 on pe4.HomeId = pe0.HomeId and pe4.EventId = 30 and pe4.Stamp > pe0.Stamp
where pe0.eventId = 12 and pe0.HomeId = 111
AND
NOT EXISTS(SELECT * FROM Phone_Events pe3
WHERE pe3.HomeId = pe0.HomeId
AND pe3.EventId not in( 13, 22 )
AND pe3.Stamp > pe0.Stamp and pe3.Stamp < pe4.Stamp );
Is there something specific to self joining that makes this slow? Is there a better way to optimize this? The killer seems to be the 'not exists' portion - this part is there to make sure there are no events between the last 'on hook' and the current 'off hook'.
EDIT: EventId's as follows:
'1', 'device connection'
'2', 'device disconnection'
'3', 'device alarm'
'11', 'ring start'
'12', 'off hook'
'13', 'hang up(other end)'
'15', 'missed call'
'21', 'caller id'
'22', 'dtmf'
'24', 'device error'
'30', 'on hook'
'31', 'ring stop'
Complete rewrite based on new information. How I approached this was to start with an inner-most query to get all records we care about based exclusively on HomeID = 111 and make sure they came back pre-sorted by the sequence ID (have index on HomeID, Sequence). As we all know, a phone call starts by picking up the phone -- eventID = 12, getting dial tone -- eventid = 22, dialing out, and someone answering, until the phone is back on the hook -- eventid = 30). If its a hangup (eventid=13), we want to ignore it.
I don't know why you are looking at the sequence # PRIOR to the current call, don't know if it really has any bearing. It looks like you are just trying to get completed calls and how long the duration. That said, I would remove the portion of the LEFT JOIN Phone_Event and the corresponding WHERE clause. It may have been there while you were just trying to figure this out.
Anyhow, back to the logic. The inner most guarantees the call sequences in order. You won't have two calls simultaneous. So by getting them in order first, I then join to the SQLVars (which creates inline variable #NextCall for the query). The purpose for this is to identify every time a new call is about to begin (EventID = 12). If so, take whatever the sequence number is, and save it. This will remain the same until the next call, so all the other "event IDs" will have the same "starting sequence ID". In addition, I'm looking for the other events... an event = 22 based on the starting sequence +1 and setting it as a flag. Then, the max time based on the start of the call (only set when eventid = 12), and end of the call (eventid = 30), and finally a flag based on your check for a hang up (eventid = 13) ie: don't consider the call if it was a hangup and no connection through.
By doing a group by, I've in essence, rolled-up each call to its own line... grouped by the home ID, and the sequence number used to initiate the actual phone call. Once THAT is done, I can then query the data and compute the call duration since the start/end time are on the same row, no self-self-self joins involved.
Finally, the where clause... Kick out any phone calls that HAD a HANG UP. Again, I don't know if you still need the element of what the starting call's time was of the last ending event.
SELECT
PreGroupedCalls.*,
timediff( PreGroupedCalls.CallEndTime, PreGroupedCalls.CallStartTime ) CallDuration
from
( SELECT
Calls.HomeID,
#NextCall := #NextCall + if( Calls.EventID = 12, Calls.Sequence, #NextCall ) as NextNewCall,
MAX( if( Calls.EventID = 12, Calls.Stamp, 0 )) as CallStartTime,
MAX( if( Calls.EventID = 30, Calls.Stamp, 0 )) as CallEndTime,
MAX( if( Calls.EventID = 22 and Calls.Sequence = #NewCallFirstSeq +1, 1, 0 )) as HadDTMFEntry,
MAX( if( Calls.EventID = 13 and Calls.Sequence = #NewCallFirstSeq +1, 1, 0 )) as WasAHangUp
from
( select pe.HomeId,
pe.Sequence,
pe.EventID,
pe.Stamp
from
Phone_Events pe
where
pe.HomeID = 111
order by
pe.Sequence ) Calls,
( select #NextCall := 0 ) SQLVars
group by
Calls.HomeID,
NextNewCall ) PreGroupedCalls
LEFT JOIN Phone_Event PriorCallEvent
ON PreGroupCalls.NextNewCall = PriorCallEvent.Sequence -1
where
PreGroupedCalls.WasHangUp = 0
AND ( PriorCallEvent.Sequence IS NULL
OR abs(timediff( PriorCallEvent.Stamp, PreGroupedCalls.CallStartTime )) > 10 )
COMMENT FROM FEEDBACK / ERROR reported
To try and fix the DOUBLE error, you obviously will need to make a slight change in the SQLVars select.. try the following
( select #NextCall := CAST( 0 as INT ) ) SQLVars
Now, what the IF() is doing... Lets take a look.
#NextCall + if(Calls.EventID = 12,Calls.Sequence, #NextCall)
means take a look at the Event ID. If it is a 12 (ie: off-hook), grab whatever the sequence number is for that entry. This will become the new "Starting Sequence" of another call. If not, just keep whatever the last value set was, as its a continuation of a call in progress. Now, lets look at some simulated data to help better illustrate all the columns
Original data Values that will ultimately be built into...
HomeID Sequence EventID Stamp #NextCall
111 1 12 8:00:00 1 beginning of a new call
111 2 22 8:00:01 1 not a new "12" event, keep last value
111 3 30 8:05:00 1 call ended, phone back on hook
111 4 12 8:09:00 4 new call, use the sequence of THIS entry
111 5 22 8:09:01 4 same call
111 6 13 8:09:15 4 same call, but a hang up
111 7 30 8:09:16 4 same call, phone back on hook
111 8 12 8:15:30 8 new call, get sequence ID
111 9 22 8:15:31 8 same call...
111 10 30 8:37:15 8 same call ending...
Now, the query SHOULD create something like this
HomeID NextNewCall CallStartTime CallEndTime HadDTMFEntry WasAHangUp
111 1 8:00:00 8:05:00 1 0
111 4 8:09:00 8:09:16 1 1
111 8 8:15:30 8:37:15 1 0
As you can see, the #NextCall keeps all the sequential entries for a given call "Grouped" together so you don't have to just use greater than span information or less than... It is always going to follow a certain path of "events", so whatever is the one that started the call is the basis for the rest of the events until the next call is started, then THAT sequence is grabbed for THAT group call.
Yup, its a lot to grasp.. but hopefully now more digestible for you :)