MYSQL Select using WHERE, AND, OR - mysql

I am trying to get a selection of data out of my database but am having trouble, I'm sure its something simple that I am not seeing but I cant figure it out.
A table, jobs, has 5 fields: job_id, job_status, job_schedulestatus, job_schedulestatus2, job_schedulestatus3. The id is a auto incremented number, the status can have two values Active or Invoiced and the schedule status can each hold a large number of values that are selected from a drop down but in this case I just want to focus on values called FOC, Cancelled and Sample.
What I am trying to do is select all values that are set to active and don't have FOC, Cancelled or Sample set in the schedule status 1-3
Here is my select statement:
SELECT job_id, job_status, job_schedulestatus, job_schedulestatus2, job_schedulestatus3
FROM jobs WHERE job_status='Active'
AND ( job_schedulestatus!='FOC' OR job_schedulestatus!='Cancelled' OR job_schedulestatus!='Sample' )
OR ( job_schedulestatus2!='FOC' OR job_schedulestatus2!='Cancelled' OR job_schedulestatus2!='Sample' )
OR ( job_schedulestatus3!='FOC' OR job_schedulestatus3!='Cancelled' OR job_schedulestatus3!='Sample' )
ORDER BY job_id DESC;
This still shows all fields that have FOC, Cancelled or Sample. Now if I remove the != and replace with just = it will only show those with FOC, Cancelled or Sample which suggests to me that there is an issue using the !=. I tried replcaing with <> but still doesn't work.
If I just test it with one check on the schedule status it works as below:
SELECT job_id, job_status, job_schedulestatus, job_schedulestatus2, job_schedulestatus3
FROM jobs WHERE job_status='Active' AND job_schedulestatus!='Cancelled
ORDER BY job_id DESC;
Any Ideas?
Thanks in advance

This seems easier to process:
SELECT job_id
, job_status
, job_schedulestatus
, job_schedulestatus2
, job_schedulestatus3
FROM jobs
WHERE job_status = 'Active'
AND
( job_schedulestatus NOT IN ('FOC','Cancelled','Sample')
OR job_schedulestatus2 NOT IN ('FOC','Cancelled','Sample')
OR job_schedulestatus3 NOT IN ('FOC','Cancelled','Sample')
)
ORDER
BY job_id DESC;
Note that generally, where you find yourself with enumerated columns (above, say, 2) you can be confident that your schema design is sub-optimal.

SELECT job_id, job_status, job_schedulestatus, job_schedulestatus2, job_schedulestatus3
FROM jobs WHERE job_status='Active'
AND (job_schedulestatus NOT IN ('FOC','Cancelled','Sample')
OR job_schedulestatus2 NOT IN ('FOC','Cancelled','Sample')
OR job_schedulestatus3 NOT IN ('FOC','Cancelled','Sample'))
ORDER BY job_id DESC;

Related

MySQL -- Finding % of orders with a transaction failure

I have an order_transactions table with 3 relevant columns. id (unique id for the transaction attempt), order_id (the id of the order for which the attempt is being made), and success an int which is 0 if failed, and 1 if successful.
There can be 0 or more failed transactions before a successful transaction, for each order_id.
The question is, how do I find:
The number of orders which never had a successful transaction
The number of orders which had a transaction with a failure (eventually successful or not)
The number of orders which never had a failed transaction (success only)
I realize this is some combination of distinct, group by, maybe a subselect, etc, I'm just not well versed in this enough. Thanks.
To get the number of orders which never had a successful transaction you can use:
SELECT COUNT(*)
FROM (
SELECT order_id
FROM transactions
GROUP BY order_id
HAVING COUNT(CASE WHEN success = 1 THEN 1 END) = 0) AS t
Demo here
The number of orders which had a transaction with a failure (eventually successful or not) can be obtained using the query:
SELECT COUNT(*)
FROM (
SELECT order_id
FROM transactions
GROUP BY order_id
HAVING COUNT(CASE WHEN success = 0 THEN 1 END) > 0) AS t
Demo here
Finally, to get the number of orders which never had a failed transaction (success only):
SELECT COUNT(*)
FROM (
SELECT order_id
FROM transactions
GROUP BY order_id
HAVING COUNT(CASE WHEN success = 0 THEN 1 END) = 0) AS t
Demo here
You want "counts" of orders that meet specific conditions over multiple rows, so I'd start with a GROUP BY order_id
SELECT ...
FROM mytable t
GROUP BY t.order_id
To find out if a particular order ever had a failed transaction, etc. we can use aggregates on expressions that "test" for conditions.
For example:
SELECT MAX(t.success=1) AS succeeded
, MAX(t.success=0) AS failed
, IF(MAX(t.success=1),0,1) AS never_succeeded
FROM mytable t
GROUP BY t.order_id
The expressions in the SELECT list of that query are MySQL shorthand. We could use longer expressions (MySQL IF() function or ANSI CASE expressions) to achieve an equivalent result, e.g.
CASE WHEN t.success = 1 THEN 1 ELSE 0 END
We could include the `order_id` column in the SELECT list for testing. We can compare the results for each order_id to the rows in the original table, to verify that the results returned meet the specification.
To get "counts" of orders, we can reference the query as an inline view, and use aggregate expressions in the SELECT list.
For example:
SELECT SUM(r.succeeded) AS cnt_succeeded
, SUM(r.failed) AS cnt_failed
, SUM(r.never_succeeded) AS cnt_never_succeeded
FROM (
SELECT MAX(t.success=1) AS succeeded
, MAX(t.success=0) AS failed
, IF(MAX(t.success=1),0,1) AS never_succeeded
FROM mytable t
GROUP BY t.order_id
) r
Since the expressions in the SELECT list return either 0, 1 or NULL, we can use the SUM() aggregate to get a count. To make use of a COUNT() aggregate, we would need to return NULL in place of a 0 (FALSE) value.
SELECT COUNT(IF(r.succeeded,1,NULL)) AS cnt_succeeded
, COUNT(IF(r.failed,1,NULL)) AS cnt_failed
, COUNT(IF(r.never_succeeded,1,NULL)) AS cnt_never_succeeded
FROM (
SELECT MAX(t.success=1) AS succeeded
, MAX(t.success=0) AS failed
, IF(MAX(t.success=1),0,1) AS never_succeeded
FROM mytable t
GROUP BY t.order_id
) r
If you want a count of all order_id, add a COUNT(1) expression in the outer query. If you need percentages, do the division and multiply by 100,
For example
SELECT SUM(r.succeeded) AS cnt_succeeded
, SUM(r.failed) AS cnt_failed
, SUM(r.never_succeeded) AS cnt_never_succeeded
, SUM(1) AS cnt_all_orders
, SUM(r.failed)/SUM(1)*100.0 AS pct_with_a_failure
, SUM(r.succeeded)/SUM(1)*100.0 AS pct_succeeded
, SUM(r.never_succeeded)/SUM(1)*100.0 AS pct_never_succeeded
FROM (
SELECT MAX(t.success=1) AS succeeded
, MAX(t.success=0) AS failed
, IF(MAX(t.success=1),0,1) AS never_succeeded
FROM mytable t
GROUP BY t.order_id
) r
(The percentages here are a comparison to the count of distinct order_id values, not as the total number of rows in the table).
successful order
select count(*) from
( select distinct order_id from my_table where success = 1 ) as t;
unsuccessful order
select count(*) from
( select distinct order_id from my_table where success = 0 ) as t;
never filed transaction
select count(*) from
( select distintc order_id from my_table where id not in
(select distinct order_id from my_table where success = 0) ) as t;

This slow MySQL Query needs improvement

This query works and provides me with the information I need, but it is very slow: it takes 18 seconds to agregate a database of only 4,000 records.
I'm bringing it here to see if anyone has any advice on how to improve it.
SELECT COUNT( status ) AS quantity, status
FROM log_table
WHERE time_stamp
IN (SELECT MAX( time_stamp ) FROM log_table GROUP BY userid )
GROUP BY status
Here's what it does/what it needs to do in plain text:
I have a table full of logs, each log contains a "userid", "status" (integer between 1-12) and "time_stamp" (a time stamp of when the log was created). There may be many entries for a particular userid, but with a different time stamp and status. I'm trying to get the most recent status (based on time_stamp) for each userid, then count the occurrences of each most-recent status among all the users.
My initial idea was to use a sub query with GROUP BY userid, that worked fast - but that always returned the first entry for each userid, not the most recent. If I could do GROUP BY userid using time_stamp DESC to Identify which row should be the representative for the group, that would be great. But of course ORDER BY inside of group does not work.
Any suggestions?
The first thing to try is to make this an explicit join:
SELECT COUNT(status) AS quantity, status
FROM log_table join
(select lg.userid, MAX( time_stamp ) as maxts
from log_table lg
GROUP BY userid
) lgu
on lgu.userid = lg.userid and lgu.maxts = lg.time_stamp
GROUP BY status;
Another approach is to use a different where clause. This will work best if you have an index on log_table(userid, time_stamp). This approach is doing the filtering by saying "there is no timestamp bigger than this one for a given user":
SELECT COUNT(status) AS quantity, status
FROM log_table
WHERE not exists (select 1
from log_table lg2
where lgu.userid = lg.userid and lg2.time_stamp > lg.time_stamp
)
GROUP BY status;

Mysql get latest row for status

I have a log table with several statuses. It logs the position of physical objects in an external system. I want to get the latest rows for a status for each distinct physical object.
I need a list of typeids and their quantity for each status, minus the quantity of typeids that have an entry for another status that is later than the row with the status we are looking for.
e.g each status move is recorded but nothing else.
Here's the problem, I don't have a distinct ID for each physical object. I can only calculate how many there are from the state of the log table.
I've tried
SELECT dl.id, dl.status
FROM `log` AS dl
INNER JOIN (
SELECT MAX( `date` ) , id
FROM `log`
GROUP BY id ORDER BY `date` DESC
) AS dl2
WHERE dl.id = dl2.id
but this would require a distinct type id to work.
My table has a primary key id, datetime, status, product type_id. There are four different statuses.
a product must pass through all statuses.
Example Data.
date typeid status id
2014-01-13 PF0180 shopfloor 71941
2014-01-13 ND0355 shopfloor 71940
2014-01-10 ND0355 machine 71938
2014-01-10 ND0355 machine 71937
2014-01-10 ND0282 machine 7193
when selected results for the status shopfloor I would want
quantity typeid
1 ND0355
1 PF0180
when selecting for status machine I would want
quantity typeid
1 ND0282
1 ND0355
The order of the statuses shouldn't matter it only matters if there is a later entry for the product.
If I understood you correctly, this will give you the desired output:
select
l1.typeid,
l1.status,
count(1) - (
select count(1)
from log l2
where l2.typeid = l1.typeid and
l2.date > l1.date
)
from log l1
group by l1.typeid, l1.status;
Check this SQL Fiddle
TYPEID STATUS TOTAL
-----------------------------
ND0282 machine 1
ND0355 machine 1
ND0355 shopfloor 1
PF0180 shopfloor 1
You need to get the greatest date per status, not per id. Then join to the log table where the status and date are the same.
SELECT dl.id, dl.status
FROM `log` AS dl
INNER JOIN (
SELECT status, MAX( `date` ) AS date
FROM `log`
GROUP BY status ORDER BY NULL
) AS dl2 USING (status, date);
It would be helpful to have an index on (status, date) on this table, which would allow the subquery to run as an index-only query.
Everton Agner originally posted this solution, but the reply seems to have disappeared so I'm adding it (with slight modifications)
select
l1.typeid,
l1.status,
count(1) - (
select count(1)
from log l2
where l2.typeid = l1.typeid and
l2.`date` > l1.`date`
AND l2.status != 'dieshop'
) as quant
from log l1
WHERE l1.status = 'dieshop'
group by l1.typeid;

Mysql Group By condition

I want to Apply condition for GROUP BY.
When the condition city_id != 0 is true, group the list. Otherwise normal list.
I used this query for that:
(
SELECT city_id, sum(sales) as counts
FROM product_sales
WHERE city_id !=0
GROUP BY city_id
)
UNION
(
SELECT city_id, sales
FROM product_sales
WHERE city_id =0
ORDER BY sales_id
)
Anyone can help me avoid the UNION and get the list in a single query?
One idea : GROUP BY the city_id when it is not zero, else emulate a random unique value for grouping with UUID(). So each row with city_id = 0 will not be grouped.
select city_id, sum(sales)
from product_sales
group by
case when city_id = 0
then UUID()
else city_id
end
SQL Fiddle.
A UUID is designed as a number that is globally unique in space and
time. Two calls to UUID() are expected to generate two different
values, even if these calls are performed on two separate computers
that are not connected to each other.

Date difference between two records in same table

I have a table (job_logs) with the following records:
id, job_id, user_id, status, created_at, job_type.
Each time a job starts to run a record is written in the job_log table with status='started'. When a job finish running another record is added to the table with status='completed'.
Both records has the same user_id, job_type and job_id (which is determined by the process running the job - unique to these 2 records).
I want a query that will return all these records pairs in the table (ordered by id desc) but the tricky part is that I want to add to the record with the 'completed' status the time it took the job to run (completed.created_at - started.created_at).
How can I do that?
SELECT j1.job_id AS job_id, (j2.created_at - j1.created_at) AS time_run
FROM job_logs j1 INNER JOIN job_logs j2 ON (j1.job_id = j2.job_id)
WHERE j1.status = 'started' AND j2.status = 'completed'