mysql: select statement giving error - mysql

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

Related

MySQL Ignoring Outliers

I have to present some data to work colleagues and i am having issues analysing it in MySQL.
I have 1 table called 'payments'. Each payment has columns for:
Client (our client e.g. a bank)
Amount_gbp (the GBP equivalent of the value of the transaction)
Currency
Origin_country
Client_type (individual or company)
I have written pretty simple queries like:
SELECT
AVG(amount_GBP),
COUNT(client) AS '#Of Results'
FROM payments
WHERE client_type = 'individual'
AND amount_gbp IS NOT NULL
AND currency = 'TRY'
AND country_origin = 'GB'
AND date_time BETWEEN '2017/1/1' AND '2017/9/1'
But what i really need to do is eliminate outliers from the average AND/OR only include results within a number of Standard Deviations from the Mean.
For example, ignore the top/bottom 10 results of 2% of results etc.
AND/OR ignore any results that fall outside of 2 STDEVs from the Mean
Can anyone help?
--- EDITED ANSWER -- TRY AND LET ME KNOW ---
Your best best is to create a TEMPORARY table with the avg and std_dev values and compare against them. Let me know if that is not feasible:
CREATE TEMPORARY TABLE payment_stats AS
SELECT
AVG(p.amount_gbp) as avg_gbp,
STDDEV(amount_gbp) as std_gbp,
(SELECT MIN(srt.amount_gbp) as max_gbp
FROM (SELECT amount_gbp
FROM payments
<... repeat where no p. ...>
ORDER BY amount_gbp DESC
LIMIT <top_numbers to ignore>
) srt
) max_g,
(SELECT MAX(srt.amount_gbp) as min_gbp
FROM (SELECT amount_gbp
FROM payments
<... repeat where no p. ...>
ORDER BY amount_gbp ASC
LIMIT <top_numbers to ignore>
) srt
) min_g
FROM payments
WHERE client_type = 'individual'
AND amount_gbp IS NOT NULL
AND currency = 'TRY'
AND country_origin = 'GB'
AND date_time BETWEEN '2017/1/1' AND '2017/9/1';
You can then compare against the temp table
SELECT
AVG(p.amount_gbp) as avg_gbp,
COUNT(p.client) AS '#Of Results'
FROM payments p
WHERE
p.amount_gbp >= (SELECT (avg_gbp - std_gbp*2)
FROM payment_stats)
AND p.amount_gbp <= (SELECT (avg_gbp + std_gbp*2)
FROM payment_stats)
AND p.amount_gbp > (SELECT min_g FROM payment_stats)
AND p.amount_gbp < (SELECT max_g FROM payment_stats)
AND p.client_type = 'individual'
AND p.amount_gbp IS NOT NULL
AND p.currency = 'TRY'
AND p.country_origin = 'GB'
AND p.date_time BETWEEN '2017/1/1' AND '2017/9/1';
-- Later on
DROP TEMPORARY TABLE payment_stats;
Notice I had to repeat the WHERE condition. Also change *2 to whatever <factor> to what you need!
Still Phew!
Each compare will check a different stat
Let me know if this is better

what am i lacking on mysql select query?

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.

how to work sql about MYSQL

I have this query:
select distinct Id,srcId
from schedule_mid as m
where Id > 100 and
not EXISTS ( select 1 from schedule_detail where id = m.srcId )
order by srcId
limit 500
In my opinion: first select one columns's Id and srcId
then look whether Id > 100 and not,,,,,,,,
then look whether have 500 numbers
if( >=500 ) then order by srcId and break;
revert first
I just guess the result but i don't know whether right or wrong;
The answer is: it depends.
Read this for more information:
click!

Getting the latest 'role-switch' timestamp from a messages table

Problem
I am looking at trying to get the lowest timestamp (earliest) after the 'side' has changed in a ticket conversation, to see how long it has been since the first reply to the latest message.
Example:
A (10:00) : Hello
A (10:05) : How are you?
B (10:06) : I'm fine, thank you
B (10:08) : How about you?
A (10:10) : I'm fine too, thank you <------
A (10:15) : I have to go now, see you around!
Now what I am looking for is the timestamp of the message indicated by the arrow. The first message after the 'side' of the conversation changed, in this case from user to support.
Example data from table "messages":
mid conv_id uid created_at message type
2750 1 3941 1341470051 Hello support
3615 1 3941 1342186946 How are you? support
4964 1 2210 1343588022 I'm fine, thank you user
4965 1 2210 1343588129 How about you? user
5704 1 3941 1344258743 I'm fine too, thank you support
5706 1 3941 1344258943 I have to go now, see you around! support
What I have tried so far:
select
n.nid AS `node_id`,
(
SELECT m_inner.created_at
FROM messages m_inner
WHERE m_inner.mid = messages.mid AND
CASE
WHEN MAX(m_support.created_at) < MAX(m_user.created_at) THEN -- latest reply from user
m_support.created_at
ELSE
m_user.created_at
END <= m_inner.created_at
ORDER BY messages.created_at ASC
LIMIT 0,1
) AS `latest_role_switch_timestamp`
from
node n
left join messages m on n.nid = messages.nid
left join messages m_user on n.nid = m_user.nid and m_user.type = 'user'
left join messages m_support on n.nid = m_support.nid and m_support.type = 'support'
GROUP BY messages.type, messages.nid
ORDER BY messages.nid, messages.created_at DESC
Preferred result:
node_id latest_role_switch_timestamp
1 1344258743
But this has not yielded any results for the subquery. Am I looking in the right direction or should I try something else? I don't know if this would be possible in mysql.
Also this uses a subquery, which, for performance reasons, is not ideal, considering this query will probably be used in overviews, meaning it would have to run that subquery for every message in the overview.
If you require any more information, please tell me, as I am at my wit's end
Join the table to a max-date summary of itself to get the messages of the last block, then use mysql's special group-by support to pick the first row from those for each conversation:
select * from (
select * from (
select m.*
from messages m
join (
select conv_id, type, max(created_at) last_created
from messages
group by 1,2) x
on x.conv_id = m.conv_id
and x.type != m.type
and x.last_created < m.created_at) y
order by created_at) z
group by conv_id
This returns the whole row that was the first message of the last block.
See SQLFiddle.
Performance will be pretty good, because there are no correlated subqueries.

SQL Request on multiple tables

I've put some information in this SQL Fiddle : http://www.sqlfiddle.com/#!2/0745c0/4/0
You'll see table structure and my request.
SELECT
cov.id AS cov_id,
cov.utilisateurs_uid AS cov_uid,
cov.timestamp_created as timestamp,
cov.invisible as invisible,
COALESCE(numreports, 0) AS numreports
FROM
cforge_covers AS cov
LEFT OUTER JOIN cforge_votes AS vot ON cov.id = vot.covers_id AND vot.utilisateurs_uid != 123456789
LEFT JOIN (SELECT rep.covers_id,
COUNT(rep.id) AS numreports
FROM cforge_reports AS rep
GROUP BY rep.covers_id) reports ON reports.covers_id = cov.id
WHERE invisible=0 AND (numreports < 2 OR numreports IS null OR valide > 0) AND cov.timestamp_created > '1370815140' AND cov.timestamp_created < '1373493540' GROUP BY cov.id
ORDER BY rand() DESC LIMIT 0,2
When a user creates a picture, a line is created in cforge_covers, with his Facebook UID, ID of the picture and a timestamp.
Cover pictures can be voted, once per user, and the votes ares stored in cforge_votes, where you can have the same covers_id multiple times and utilisateurs_uid (users_id) only once per covers_id max.
Cover pictures can also be reported on the same basis as votes : once per user.
The purpose of my request is to fetch :
two random covers
which haven't been reported or moderated (values stored in cforge_reports)
for which the user hasn't already voted
The last part is causing my problem : I try to exclude a specific utilisateurs_uid but as many users vote for an image, other votes for the cover make it valid to show even if the user has already voted for it.
How can I write this request better ?
Thanks in advance for your time !
You want to make use of WHERE NOT EXISTS rather than LEFT JOIN. It is unclear if you want an item that has never been moderatred, or one that has been moderated once or none (your requirements and code say different things). If you truly want one that has never been moderated, then try NOT EXISTS in both cases.
Also, I would suggest that you be diligent about formatting and aliasing All The Things. It will help those reading your code and even you, when you go back to look at it later.
SELECT cov.id AS cov_id
,cov.utilisateurs_uid AS cov_uid
,cov.timestamp_created as timestamp
,cov.invisible as invisible
,COALESCE(reports.numreports, 0) AS numreports
FROM cforge_covers AS cov
LEFT JOIN (SELECT rep.covers_id,
COUNT(rep.id) AS numreports
FROM cforge_reports AS rep
GROUP BY rep.covers_id) reports ON reports.covers_id = cov.id
WHERE cov.invisible=0
AND (reports.numreports < 2
OR reports.numreports IS null
OR ????.valide > 0)
AND cov.timestamp_created > '1370815140'
AND cov.timestamp_created < '1373493540'
AND NOT EXISTS ( SELECT 1 FROM cforge_votes vot WHERE cov.id = vot.covers_id AND vot.utilisateurs_uid = 123456789)
ORDER BY RAND() DESC LIMIT 0,2