what am i lacking on mysql select query? - mysql

what am I doing is checking if the signatoryid from the user input has the lowest level from the table which status = 1 and group by tracknum
so if the input of the user is not the lowest level on that tracknum it will not give output. but if it has the lowest level. it will give output
so far this is my query this works for signatoryid-LHPL004
but doesnt work for other signatory id like LHPL003 and so on. so whats lack with mysql code? thank you
SELECT `tracknum`, `signatoryid`, `signatorylevel`, `status`
from tble_transaction
where `status` = "1" and signatoryid = "LHPL004" and `signatorylevel` = (select MIN(`signatorylevel`) from tble_transaction where `status`="1" )
this is my expected output when i use LHPL003 as signatory ID
it didnt print out the one with tracknum DOC009 with signatoryID because the level of LHPL003 is 3 and on that tracknum DOC009 there is someone with lower level whose status is still 1 not 0

Probably you want the corresponding row for each tracknum having the minimum signatorylevel with status=1.
This assumption is based on your comment:
what i want to do sir is show only the ones with the lowest level on
each tracknum which status = 1.
The following query might suffice :
SELECT
tble_transaction.*
FROM tble_transaction
INNER JOIN
(
SELECT
tracknum,
MIN(signatorylevel) min_signatorylevel
FROM tble_transaction
WHERE `status` = "1"
GROUP BY tracknum
) AS t
ON tble_transaction.tracknum = t.tracknum AND
tble_transaction.signatorylevel = t.min_signatorylevel;
Output:
tracknum signatoryid datesigned datereceived signed status signatorylevel
DOC009 LHPL004 0/0/0000 0/0/0000 Released 1 2
DOC010 LHPL003 0/0/0000 0/0/0000 Released 1 3
Demo Here
Note:
Look, in your sample data although minimum signatorylevelis 1 for both tracknum DOC009 AND DOC010 but they have status = 0. So they won't appear in the result.

The subquery
select MIN(signatorylevel) from tble_transaction where status = "1"
returns MIN(signatorylevel) for the entire table. Specifically, it returns the value 2, and only signatoryid LHPL004 has a record with that signatorylevel, according to your sample data.
I think this should work, but I haven't tested it:
SELECT t1.tracknum, t1.signatoryid, t1.signatorylevel, t1.`status`
FROM tble_transaction t1
WHERE t1.`status` = "1"
AND t1.signatoryid = "LHPL004"
AND t1.signatorylevel =
(SELECT MIN(t2.signatorylevel)
FROM tble_transaction t2
WHERE t2.`status` = "1"
AND t2.tracknum = t1.tracknum
);
This solution uses a correlated subquery, where the subquery is run once for each record returned by the outer query. (See the last AND subclause of the subquery's WHERE clause.)
Any correlated subquery can be rewritten as a join, and vice versa, so (assuming your DBMS has a good optimizer) the only significant difference between my solution and 1000111's is personal preference.

Only LHPL004 will generate an output because it is the only one with the lowest signatorylevel and status of 1. Anything else including LHPL003 has not the lowest signatorylevel so it wont output anything and thus your code is working correctly.

Related

Selecting rows until a column value isn't the same

SELECT product.productID
, product.Name
, product.date
, product.status
FROM product
INNER JOIN shelf ON product.sheldID=shelf.shelfID
WHERE product.weekID = $ID
AND product.date < '$day'
OR (product.date = '$day' AND shelf.expire <= '$time' )
ORDER BY concat(product.date,shelf.expire)
I am trying to stop the SQL statement at a specific value e.g. bad.
I have tried using max-date, but am finding it hard as am making the time stamp in the query. (Combining date/time)
This example table shows that 3 results should be returned and if the status "bad" was the first result than no results should be returned. (They are ordered by date and time).
ProductID Date status
1 2017-03-27 Good
2 2017-03-27 Good
3 2017-03-26 Good
4 2017-03-25 Bad
5 2017-03-25 Good
Think I may have fixed it, I added this to my while loop.
The query gives the results in order by present to past using date and time, this while loop checks if the column of that row is equal to 'bad' if it is does something (might be able to use an array to fill it up with data). If not than the loop is broken.
I know it doesn't seem ideal but it works lol
while ($row = mysqli_fetch_assoc($result)) {
if ($row['status'] == "bad") {
$counter += 1;
}
else{
break;}
I will provide an answer just with your output as if it was just one table. It will give you the main ideia in how to solve your problem.
Basically I created a column called ord that will work as a row_number (MySql doesn't support it yet AFAIK). Then I got the minimum ord value for a bad status then I get everything from the data where ord is less than that.
select y.*
from (select ProductID, dt, status, #rw:=#rw+1 ord
from product, (select #rw:=0) a
order by dt desc) y
where y.ord < (select min(ord) ord
from (select ProductID, status, #rin:=#rin+1 ord
from product, (select #rin:=0) a
order by dt desc) x
where status = 'Bad');
Result will be:
ProductID dt status ord
-------------------------------------
1 2017-03-27 Good 1
2 2017-03-27 Good 2
3 2017-03-26 Good 3
Also tested with the use case where the Bad status is the first result, no results will be returned.
See it working here: http://sqlfiddle.com/#!9/28dda/1

mysql: select statement giving error

I know that I'm using wrong title because I can't think of a proper term for my problem. I am doing a document tracking system.
Here is the problem: the document need to be signed by the lower personnel first before the next personnel who is in a higher position.
In order to do that, I came up with the idea of using level on my table.
But I lack MySQL knowledge. This is my MySQL table:
This is my MySQL query. The problem is, I don't want to show to
LHPL003 the document yet because the other signatorylevel lower than his has not yet signed the document (colum status will become 0 if the signatory signed the document).
How can I do that?
SELECT `tracknum`, `signatoryid`, `signatorylevel`, `status`
from tble_transaction
where signatoryid = "LHPL003" and signed = 'Released' and signatorylevel <= "3";
question number 2:
i have problem with my first question
now i have this table
how can i do this. example the user with the level 1 already signed the document so it become
LHPL005 status = 0
what i want is a query that only LHPL004 can see the document
LHPL003 and LHPL002 cant see the document first because those who have lower lever than theirs didn't signed yet the document
SELECT `tracknum`, `signatoryid`, `signatorylevel`, `status`
from tble_transaction
where signatoryid != "LHPL003" and signed = 'Released' and status='0' and signatorylevel <= "3";
To display the rows having status as 0 and having minimum signatory level (which is input from the user)
SELECT tracknum, signatoryid,signatorylevel, status from track where status=0 and signatorylevel < 4 order by signatorylevel limit 1;
EDIT:
The above query will result only 1 record if you want multiple records use the following query
select * from track where signatorylevel = (select min(signatorylevel) from track where status = 0 and signatorylevel < 4 );
I do not understand exactly what you want, But I think one of those 2 queries has the solution for you:
SELECT * FROM `tble_transaction` as t1
WHERE signatoryid = 'LHPL003' and signed = 'Released'
AND EXISTS (select 1 from tble_transaction where signatorylevel < t1.signatorylevel and status = 0)
Second one:
Select * from tble_transaction where
signatorylevel < (SELECT min(signatory level) FROM `tble_transaction` as t1 WHERE signatoryid = 'LHPL003' and signed = 'Released' and status = 1)
ORDER BY signatory level desc
Limit 1
Second question:
SELECT * FROM `tble_transaction` as t1
WHERE signatoryid = 'LHPL005' and signed = 'Released'
AND NOT EXISTS (select 1 from tble_transaction where signatorylevel < t1.signatorylevel and status = 1)
-- ORDER BY signatorylevel LIMIT 1 -- you can ignore this if signatoryid is UNIQUE

query optimization for mysql

I have the following query which takes about 28 seconds on my machine. I would like to optimize it and know if there is any way to make it faster by creating some indexes.
select rr1.person_id as person_id, rr1.t1_value, rr2.t0_value
from (select r1.person_id, avg(r1.avg_normalized_value1) as t1_value
from (select ma1.person_id, mn1.store_name, avg(mn1.normalized_value) as avg_normalized_value1
from matrix_report1 ma1, matrix_normalized_notes mn1
where ma1.final_value = 1
and (mn1.normalized_value != 0.2
and mn1.normalized_value != 0.0 )
and ma1.user_id = mn1.user_id
and ma1.request_id = mn1.request_id
and ma1.request_id = 4 group by ma1.person_id, mn1.store_name) r1
group by r1.person_id) rr1
,(select r2.person_id, avg(r2.avg_normalized_value) as t0_value
from (select ma.person_id, mn.store_name, avg(mn.normalized_value) as avg_normalized_value
from matrix_report1 ma, matrix_normalized_notes mn
where ma.final_value = 0 and (mn.normalized_value != 0.2 and mn.normalized_value != 0.0 )
and ma.user_id = mn.user_id
and ma.request_id = mn.request_id
and ma.request_id = 4
group by ma.person_id, mn.store_name) r2
group by r2.person_id) rr2
where rr1.person_id = rr2.person_id
Basically, it aggregates data depending on the request_id and final_value (0 or 1). Is there a way to simplify it for optimization? And it would be nice to know which columns should be indexed. I created an index on user_id and request_id, but it doesn't help much.
There are about 4907424 rows on matrix_report1 and 335740 rows on matrix_normalized_notes table. These tables will grow as we have more requests.
First, the others are right about knowing better how to format your samples. Also, trying to explain in plain language what you are trying to do is also a benefit. With sample data and sample result expectations is even better.
However, that said, I think it can be significantly simplified. Your queries are almost completely identical with the exception of the one field of "final_value" = 1 or 0 respectively. Since each query will result in 1 record per "person_id", you can just do the average based on a CASE/WHEN AND remove the rest.
To help optimize the query, your matrix_report1 table should have an index on ( request_id, final_value, user_id ). Your matrix_normalized_notes table should have an index on ( request_id, user_id, store_name, normalized_value ).
Since your outer query is doing the average based on an per stores averages, you do need to keep it nested. The following should help.
SELECT
r1.person_id,
avg(r1.ANV1) as t1_value,
avg(r1.ANV0) as t0_value
from
( select
ma1.person_id,
mn1.store_name,
avg( case when ma1.final_value = 1
then mn1.normalized_value end ) as ANV1,
avg( case when ma1.final_value = 0
then mn1.normalized_value end ) as ANV0
from
matrix_report1 ma1
JOIN matrix_normalized_notes mn1
ON ma1.request_id = mn1.request_id
AND ma1.user_id = mn1.user_id
AND NOT mn1.normalized_value in ( 0.0, 0.2 )
where
ma1.request_id = 4
AND ma1.final_Value in ( 0, 1 )
group by
ma1.person_id,
mn1.store_name) r1
group by
r1.person_id
Notice the inner query is pulling all transactions for the final value as either a zero OR one. But then, the AVG is based on a case/when of the respective value for the normalized value. When the condition is NOT the 1 or 0 respectively, the result is NULL and is thus not considered when the average is computed.
So at this point, it is grouped on a per-person basis already with each store and Avg1 and Avg0 already set. Now, roll these values up directly per person regardless of the store. Again, NULL values should not be considered as part of the average computation. So, if Store "A" doesn't have a value in the Avg1, it should not skew the results. Similarly if Store "B" doesnt have a value in Avg0 result.

GROUP BY HAVING not working as expected

I'm struggling with what should be a simple query.
An event table stores user activity in an application. Each click generates a new event and datetime stamp. I need to show a list of recently accessed records having the most recent datetime stamp. I need to only show the past 7 days of activity.
The table has an auto-increment field (eventID), which corresponds with the date_event field, so it's better to use that for determining the most recent record in the group.
I found that some records are not appearing in my results with the expected most recent datetime. So I stripped my query down the basics:
NOTE that the real-life query does not look at custID. I am including it here to narrow down on the problem.
SELECT
el.eventID,
el.custID,
el.date_event
FROM
event_log el
WHERE
el.custID = 12345 AND
el.userID=987
GROUP BY
el.custID
HAVING
MAX( el.eventID )
This is returned:
eventID custID date_event
346290 12345 2013-06-21 09:58:44
Here's the EXPLAIN
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE el ref userID,custID,Composite custID 5 const 203 Using where
If I change the query to use HAVING MIN, the results don't change.. I should see a different eventID and date_event, as there are dozens of records matching the custID and userID.
SELECT
el.eventID,
el.custID,
el.date_event
FROM
event_log el
WHERE
el.custID = 12345 AND
el.userID=987
GROUP BY
el.custID
HAVING
MIN( el.eventID )
Same results as before:
eventID custID date_event
346290 12345 2013-06-21 09:58:44
No change.
This tells me I have another problem, but I am not seeing what that might be.
Some pointers would be appreciated.
SELECT
el.eventID,
el.custID,
el.date_event
FROM
event_log el
WHERE
el.custID = 12345 AND
el.userID=987 AND
el.eventID IN (SELECT MAX(eventID)
FROM event_log
WHERE custID = 12345
AND userID = 987)
Your query doesn't work because you misunderstand what HAVING does. It evaluates the expression on each line of the result set, and keeps the rows where the expression evaluates to true. The expression MAX(el.eventID) simply returns the maximum event ID selected by the query, it doesn't compare the current row to that event ID.
Another way is:
SELECT
el.eventID,
el.custID,
el.date_event
FROM
event_log el
WHERE
el.custID = 12345 AND
el.userID=987
ORDER BY eventID DESC
LIMIT 1
The more general form that works for multiple custID is:
SELECT el.*
FROM event_log el
JOIN (SELECT custID, max(date_event) maxdate
FROM event_log
WHERE userID = 987
GROUP BY custID) emax
ON el.custID = emax.custID AND el.date_event = emax.maxdate
WHERE el.userID = 987
You can use a group function in a statement containing no GROUP BY clause, but it would be equivalent to grouping on all rows. But I guess you're looking for the common syntax,
SELECT
MIN(el.eventID) AS `min_eventID`, --> Yes it is wrong :(
el.custID,
el.date_event
FROM
event_log el
WHERE
el.userID = 987
GROUP BY el.custID;
But disagreements are welcome .
[ Edit ]
I think I didn't show a solution fast enough... but maybe you're rather looking for the fastest solution.
Assuming field date_event defaults to CURRENT_TIMESTAMP (am I wrong?), ordering by date_event would be a waste of time (and money, thus).
I've made some tests with 20K rows and execution time was about 5ms.
SELECT STRAIGHT_JOIN y.*
FROM ((
SELECT MAX(eventId) as eventId
FROM event_log
WHERE userId = 987 AND custId = 12345
)) AS x
INNER JOIN event_log AS y
USING (eventId);
Maybe (possibly, who knows) you didn't get the straight_join thing; as documented on the scriptures, STRAIGHT_JOINs are similar to JOINs, except that the left table is always read before the right table. Sometimes it's useful.
For your specific situation, we're likely to filter to a certain eventID before (on table "x"), not to retrieve 99,99% useless rows from table "y".
More disagreements expected in 3, 2, ...

MySQL Complicated SELECT

I have a MySQL table (tbl_filters) with 3 columns: id, cat, val
id & val are numeric, cat is varchar. There are multiple rows for each id.
I also have another table (tbl_info) with multiple columns, including an id which corresponds to the id from tbl_filters. There is a column called name, which is what I'm looking for.
I would like to select the name of all the rows which match a set value for cat, but only if the val for cat is the maximum for this id, and only if it is above a minimum set val.
In pseudocode it would be something like:
SELECT tbl_info.name FROM tbl_info,tbl_filters
WHERE (tbl_info.id=tbl_filters.id) AND (cat="mycat") AND (val>=0.3)
AND (there are no other rows for this id in tbl_info with a higher value for val)
Example:
tbl_filters
id,cat,val
1 eg1 0.43
1 eg2 0.60
1 eg3 0.78
tbl_info
id name
1 MyName
In the above example, a value should only be returned if I am looking for the cat called eg3, since that has the highest value. For the other cats, nothing should be returned, since they are not the highest value.
Another option would be to make a column in tbl_info just for the cat with the highest value, but that is a messy solution I would prefer to avoid.
I THINK I'm following you... The INNER-MOST query pre-qualifies the HIGHEST Value per ID of your minimum value qualification, and the category that qualifies. ONCE you get that list, re-join back to get the name from the tbl_info. I've re-joined to the tbl_filters a second time in case there were other elements on that record you want, such as the date of the rate, or other things. If you DONT need that, you can ignore the second "tf2" join and just change the fields list from tf2.val to PreQualified.HighestQualVal.
select
ti.id,
ti.name,
tf2.val
from
( select
tf.id,
max( tf.val ) as HighestQualVal
from
tbl_filters tf
where
tf.cat = "mycat"
and tf.val >= 0.3
) PreQualified
JOIN tbl_info ti
on PreQualified.id = ti.id
JOIN tbl_filters tf2
on PreQualified.id = tf2.id
AND PreQualified.HighestQualVal = tf2.val
What about?
select ti.name, MaxId.maxVal from
(select tf1.id, tf1.cat, max(tf1.val) as maxVal from tbl_filters1 tf1
where tf1.cat = 'eg3' and tf1.val >= 0.0
group by tf1.id, tf1.cat) MaxCat
inner join (
select tf2.id, max(tf2.val) as maxVal from tbl_filters2 tf2
group by tf2.id) MaxId
on (MaxCat.id = MaxId.id and MaxCat.maxVal = MaxId.maxVal)
inner join tbl_info ti on MaxId.id = ti.id
Example here
Basically, and if I'm not wrong (again), I'm getting all the maximum val per each id and cat pair. Then get the maximum val for each id. If both match, i.e. if the max for the cat is the same as the max for the whole id, then I return the results.
Feel free to correct me if I'm wrong.