Optimizing Slow SQL Query - mysql

I have a query and it uses MySQL as its database. Now I'm going nuts as to why it took 11 minutes to render result, I have like 7K+ records on my table and running a recursive subquery(?), I'm not entirely sure if this is what eats up the memory but if you could look and give some remedy as to how can this be optimized I would be very happy and contented...
My goal is to compress records relative to a particular person... And rank them based on time finished.
Table structure:
CREATE TABLE `temptbl_raceeventresult_step1` (
`RunCatEventId` INT(11) NOT NULL,
`RunEventId` INT(11) NOT NULL,
`CategoryId` INT(11) NOT NULL,
`FullName` VARCHAR(255) NOT NULL,
`BIB` INT(11) NOT NULL,
`Category` DECIMAL(16,3) NOT NULL,
`Ord` INT(11) NOT NULL,
`TransitionId` INT(11) NOT NULL,
`Time` DATETIME NOT NULL)
COLLATE='latin1_swedish_ci'ENGINE=MyISAM;
//Query
SELECT
UCASE(g1.Name) Name
,g1.BIB
,g1.TimeDerived
,g1.CategoryName
,g1.TotalPace
,g1.Category
,COUNT(*) AS rank
FROM
(
SELECT
a.FullName Name,
a.RunCatEventId,
a.RunEventId,
a.CategoryId,
a.BIB,
a.Category,
a.Ord,
a.TransitionId,
SEC_TO_TIME(SUM(TIME_TO_SEC(Time) - (SELECT TIME_TO_SEC(Time) TimeMinuend FROM temptbl_raceeventresult_step1 WHERE BIB = a.bib AND (Ord + 1) = a.Ord AND CategoryId = a.CategoryId))) TimeDerived,
SEC_TO_TIME(SUM(TIME_TO_SEC(Time) - (SELECT TIME_TO_SEC(Time) TimeMinuend FROM temptbl_raceeventresult_step1 WHERE BIB = a.bib AND (Ord + 1) = a.Ord AND CategoryId = a.CategoryId)) / a.Category) TotalPace
FROM temptbl_raceeventresult_step1 a
JOIN (SELECT #cat:=null) xyz
GROUP BY a.RunCatEventId, a.RunEventId, a.CategoryId, a.FullName, a.BIB
) g1
JOIN
(
SELECT
a.FullName Name,
a.RunCatEventId,
a.RunEventId,
a.CategoryId,
a.BIB,
a.Category,
a.Ord,
a.TransitionId,
SEC_TO_TIME(SUM(TIME_TO_SEC(Time) - (SELECT TIME_TO_SEC(Time) TimeMinuend FROM temptbl_raceeventresult_step1 WHERE BIB = a.bib AND (Ord + 1) = a.Ord AND CategoryId = a.CategoryId))) TimeDerived,
SEC_TO_TIME(SUM(TIME_TO_SEC(Time) - (SELECT TIME_TO_SEC(Time) TimeMinuend FROM temptbl_raceeventresult_step1 WHERE BIB = a.bib AND (Ord + 1) = a.Ord AND CategoryId = a.CategoryId)) / a.Category) TotalPace
FROM temptbl_raceeventresult_step1 a
JOIN (SELECT #cat:=null) xyz
GROUP BY a.RunCatEventId, a.RunEventId, a.CategoryId, a.FullName, a.BIB
) g2
ON (g2.TimeDerived, g2.bib) <= (g1.TimeDerived, g1.bib) AND g1.Category = g2.Category
WHERE g2.Category = 5 AND g1.RunEventId = 3
GROUP BY g1.Name
,g1.BIB
,g1.TimeDerived
,g1.CategoryName
,g1.TotalPace
,g1.Category
ORDER BY g1.Category, rank

Related

Joining a single row to a query

I need to get a value from another table but where there may be 5/6 results, I only need to show the latest one. I've tried the following:
SELECT s.Mileage
, s.PurchasePrice
, v.make
, v.model
, v.vrm
, c.CleanLive
FROM StockBook s
LEFT
JOIN Vehicles v
ON v.VehicleID = s.VehicleID
LEFT
JOIN CapVals c
ON c.LeadID = (SELECT C1.CleanLive
FROM CapVals C1
WHERE s.LeadID = c.LeadID
ORDER
BY C1.Date
LIMIT 1
)
ORDER
BY StockBookID
Which is working as a query but not showing CleanLive value.
I've set up a sample data set and DB Fiddle here:
CREATE TABLE `Vehicles` (
`VehicleID` int(11) NOT NULL,
`vrm` varchar(15) NOT NULL,
`make` varchar(40) NOT NULL,
`model` varchar(40) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `StockBook` (
`StockBookID` int(11) NOT NULL,
`VehicleID` int(11) NOT NULL,
`LeadID` int(11) NOT NULL,
`Mileage` int(11) NOT NULL,
`PurchasePrice` decimal(15,2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `CapVals` (
`CapValsID` int(11) NOT NULL,
`LeadID` int(11) DEFAULT NULL,
`CleanLive` int(11) DEFAULT NULL,
`Date` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `Vehicles` (`VehicleID`, `vrm`, `make`, `model`) VALUES
(1, 'M900WRD', 'Vauxhall', 'Signum');
INSERT INTO `StockBook` (`StockBookID`, `LeadID`, `VehicleID`, `Mileage`, `PurchasePrice`) VALUES
(1, 1, 1, 17000, 15000.00);
INSERT INTO `CapVals` (`CapValsID`, `LeadID`, `CleanLive`, `Date`) VALUES
(6455, 1, 1540, '2019-12-04 15:02:29'),
(6456, 1, 1540, '2019-12-04 15:02:29'),
(6457, 1, 1540, '2019-12-04 15:02:29');
https://www.db-fiddle.com/f/b4fQuMVpXHGxqgYJ4ia92w/4
You can try this
SELECT Stock.Mileage, Stock.PurchasePrice, Vehi.make, Vehi.model, Vehi.vrm,
(SELECT CleanLive from CapVals a WHERE a.LeadID = Stock.LeadID ORDER BY DATE DESC LIMIT 1) AS CleanLive
FROM StockBook Stock
LEFT JOIN Vehicles Vehi
ON Stock.VehicleID=Vehi.VehicleID
ORDER BY StockBookID
SELECT v.vehicleID
, v.vrm
, v.make
, v.model
, s.stockbookid
, s.leadid
, s.mileage
, s.purchaseprice
, c.capvalsid
, c.cleanlive
, c.date
FROM vehicles v
JOIN stockbook s
ON s.vehicleid = v.vehicleid
JOIN capvals c
ON c.leadid = s.leadid
JOIN
( SELECT leadid,MAX(capvalsid) capvalsid FROM capvals GROUP BY leadid ) x
ON x.leadid = c.leadid
AND x.capvalsid = c.capvalsid;
+-----------+---------+----------+--------+-------------+--------+---------+---------------+-----------+-----------+---------------------+
| vehicleID | vrm | make | model | stockbookid | leadid | mileage | purchaseprice | capvalsid | cleanlive | date |
+-----------+---------+----------+--------+-------------+--------+---------+---------------+-----------+-----------+---------------------+
| 1 | M900WRD | Vauxhall | Signum | 1 | 1 | 17000 | 15000.00 | 6457 | 1540 | 2019-12-04 15:02:29 |
+-----------+---------+----------+--------+-------------+--------+---------+---------------+-----------+-----------+---------------------+
Use Row_number concept, which will avoid the duplicate rows and give you recent one,
FIDDLE DEMO
SELECT Stock.Mileage, Stock.PurchasePrice, Vehi.make, Vehi.model, Vehi.vrm,
X.CleanLive as CleanLive
FROM StockBook Stock
LEFT JOIN Vehicles Vehi ON Stock.VehicleID=Vehi.VehicleID
LEFT JOIN (SELECT #LeadID:=LeadID,C1.LeadID, CleanLive, C1.Date, #row_number:=CASE WHEN #LeadID = LeadID THEN #row_number + 1 ELSE 1 END AS num
FROM CapVals AS C1, (SELECT #LeadID:=0,#row_number:=0) as t ORDER BY C1.Date DESC) X ON X.LeadID = Stock.LeadID AND X.num = 1
ORDER BY StockBookID

Find percentage of Wins/Draw/Losses of last X games played by a team

I have a table (test_matches) with a record of the results of several games, sorted by date.
GHFT = Goals Home Team Full Time.
GAFT = Goals Away Team Full Time.
CREATE TABLE `test_matches` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`match_date` date NOT NULL,
`home_team` varchar(250) DEFAULT NULL,
`away_team` varchar(250) DEFAULT NULL,
GHFT` int(11) NOT NULL DEFAULT '0',
`GAFT` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
);
INSERT INTO test_matches (match_date, home_team, away_team, GHFT, GAFT )
VALUES ('2019-01-01', 'Real Madrid', 'Zaragoza', 2,0),
('2019-01-03', 'Barcelona', 'Lugo', 1,1),
('2019-01-04', 'Real Madrid', 'Lugo', 2,1),
('2019-01-05', 'Barcelona', 'Compostela', 4,1),
('2019-01-06', 'Real Madrid', 'Barcelona', 0,2),
('2019-01-07', 'Barcelona', 'Zaragoza', 0,0);
http://sqlfiddle.com/#!9/c0f16a/1
I tried this query:
SELECT home_team,
ROUND(SUM(CASE WHEN ghft > gaft = 1 THEN 1 ELSE 0 END) /COUNT(*) *100) AS W_Home_Team,
ROUND(SUM(CASE WHEN ghft = gaft = 1 THEN 1 ELSE 0 END) /COUNT(*) *100) AS D_Home_Team,
ROUND(SUM(CASE WHEN ghft < gaft = 1 THEN 1 ELSE 0 END) /COUNT(*) *100) AS L_Home_Team
FROM ( SELECT home_team, ghft, gaft FROM test_matches ORDER BY id DESC LIMIT 2) average
GROUP BY home_team;
However, the result I get is not correct, since it is taking into account the last two records of the table, not the last two records of each team.
The correct result is:
Barcelona 50-50-0 and Real Madrid 50-0-50.
How can I calculate the percentages of the last 2 matches of each team?
With this SELECT Statement
SELECT
home_team
, ROUND(SUM(IF(ghft > gaft,1,0)) / COUNT(*),2) * 100 W_Home_Team
, ROUND(SUM(IF(ghft = gaft,1,0)) / COUNT(*),2) * 100 D_Home_Team
, ROUND(SUM(IF(ghft < gaft,1,0)) / COUNT(*),2) * 100 L_Home_Team
FROM
(SELECT
home_team
,match_date
, ghft
, gaft
, IF(#team <> home_team,#rank := 0, #rank := #rank) dec1
,#rank := #rank+1 rnk
,#team := home_team
FROM test_matches,(SELECT #rank := 0) r1,(SELECT #team := '') r2
ORDER BY home_team,match_date) t1
WHERE rnk < 3
GROUP BY home_team
;
You get this result
home_team W_Home_Team D_Home_Team L_Home_Team
Barcelona 50 50 0
Real Madrid 100 0 0
This rounds the division to two digit so that you will get 25,25 Procent. You will have to adept this to your needs.
The inner SELECT is only to get the correct number of games per club.
with WHERE rnk < 3 you can change the number of games you need
SQL Fiddle example

is there a better way to write the query that I wrote

I have the following query which gives me the overall status of a project. I was wondering if there was a better way to write the same query.
an example schema for the table:
CREATE TABLE `test_status`
(
`test_id` int
(11) NOT NULL,
`group_id` int
(11) NOT NULL,
`sub_test_id` int
(11) NOT NULL,
`project_id` int
(11) NOT NULL,
`collection_status` varchar
(20) DEFAULT NULL,
`labeling_status` varchar
(20) DEFAULT NULL,
`upload_status` varchar
(20) DEFAULT NULL,
`upload_date` date DEFAULT NULL,
`disk_number` int
(11) DEFAULT NULL,
`subject_id` varchar
(10) DEFAULT NULL,
`collection_id` varchar
(25) DEFAULT NULL,
`assigned_to` int
(11) DEFAULT NULL,
`assigned_on` date DEFAULT NULL,
`turned_in_date` date DEFAULT NULL,
PRIMARY KEY
(`test_id`,`group_id`,`sub_test_id`,`project_id`));
My Query:
select
(select count(*)
from test_status
where project_id = 7 and collection_status = 'COLLECTED') as collected,
(select count(*)
from test_status
where project_id = 7 and collection_status = 'SCHEDULED') as scheduled,
(select count(*)
from test_status
where project_id = 7 and collection_status = 'CORRUPTED') as corrupted,
(select count(*)
from test_status
where project_id = 7 and collection_status is NULL) as 'not collected',
(select count(*)
from test_status
where project_id = 7 and ((labeling_status = 'GOOD' and (upload_status != 'UPLOADED' or upload_status != 'QUEUED')) or (labeling_status = 'Labeled'))) as 'Labeled',
(select count(*)
from test_status
where project_id = 7 and (labeling_status = 'RAW') or (labeling_status = 'ASSIGNED') ) as 'to be labeled',
(select count(*)
from test_status
where project_id = 7 and labeling_status = 'RE-LABEL') as 'Re-label',
(select count(*)
from test_status
where project_id = 7 and (upload_status = 'UPLOADED' or upload_status = 'QUEUED')) as 'Uploaded',
(select count(*)
from test_status
where project_id = 7 and labeling_status = 'GOOD' and upload_status is null) as 'ready to be uploaded';
You can try to use condition aggregate function will be better.
let the condition in CASE WHEN
SELECT
COUNT(CASE WHEN collection_status = 'COLLECTED' THEN 1 END),
COUNT(CASE WHEN collection_status = 'SCHEDULED' THEN 1 END),
COUNT(CASE WHEN collection_status = 'CORRUPTED' THEN 1 END),
COUNT(CASE WHEN collection_status is NULL THEN 1 END),
COUNT(CASE WHEN ((labeling_status = 'GOOD' and (upload_status != 'UPLOADED' or upload_status != 'QUEUED')) or (labeling_status = 'Labeled'))THEN 1 END),
COUNT(CASE WHEN (labeling_status = 'RAW') or (labeling_status = 'ASSIGNED') THEN 1 END),
COUNT(CASE WHEN labeling_status = 'RE-LABEL' THEN 1 END),
COUNT(CASE WHEN (upload_status = 'UPLOADED' or upload_status = 'QUEUED') THEN 1 END),
COUNT(CASE WHEN labeling_status = 'GOOD' and upload_status is null THEN 1 END)
FROM test_status
WHERE project_id = 7
Or you can try a simpler way, SUM with bool (0 or 1) to count
SELECT
SUM(collection_status = 'COLLECTED'),
SUM(collection_status = 'SCHEDULED'),
SUM(collection_status = 'CORRUPTED' ),
SUM(collection_status is NULL ),
SUM(((labeling_status = 'GOOD' and (upload_status != 'UPLOADED' or upload_status != 'QUEUED')) or (labeling_status = 'Labeled'))),
SUM((labeling_status = 'RAW') or (labeling_status = 'ASSIGNED') ),
SUM(labeling_status = 'RE-LABEL' ),
SUM((upload_status = 'UPLOADED' or upload_status = 'QUEUED')),
SUM(labeling_status = 'GOOD' and upload_status is null )
FROM test_status
WHERE project_id = 7

How to write condition for subquery alias if having null value

Here is my query,
SELECT
`h`.`hotel_id`,
(
SELECT COUNT(room_id)
FROM
`abserve_hotel_rooms` AS `rm`
WHERE
`rm`.`adults_count` >= "1" AND `rm`.`room_count` >= "1" AND "Available" = IF(
check_in_time = '2016-03-15',
'Unavailable',
(
IF(
'2016-03-15' > check_in_time,
(
IF(
'2016-03-15' < check_out_time,
'Unavailable',
'Available'
)
),
(
IF(
'2016-03-22' > check_in_time,
'Unavailable',
'Available'
)
)
)
)
) AND `room_prize` BETWEEN '174' AND '600' AND `rm`.`hotel_id` = `h`.`hotel_id`
) AS `avail_room_count`,
(
SELECT MIN(room_prize)
FROM
`abserve_hotel_rooms` AS `rm`
WHERE
`rm`.`adults_count` >= "1" AND `rm`.`room_count` >= "1" AND "Available" = IF(
check_in_time = '2016-03-15',
'Unavailable',
(
IF(
'2016-03-15' > check_in_time,
(
IF(
'2016-03-15' < check_out_time,
'Unavailable',
'Available'
)
),
(
IF(
'2016-03-22' > check_in_time,
'Unavailable',
'Available'
)
)
)
)
) AND `room_prize` BETWEEN '174' AND '600' AND `rm`.`hotel_id` = `h`.`hotel_id`
) AS `min_val`
FROM
`abserve_hotels` AS `h`
WHERE
1 AND `city` = "madurai" AND `country` = "india"
It totally return one column values from my table abserve_hotels which is hotel_id with extra two alias columns such as avail_room_count and min_val..
And I wrote those in a subquery..
Here I have to check a condition WHERE min_val IS NOT NULL .i.e; if min_val having NULL value I have to restrict it
How can I do this..
And this is my table
hotel_id avail_room_count min_val
1 0 NULL
2 0 NULL
Here I need to restrict these NULL values..
Someone please help me ..
Add a HAVING clause at the end:
HAVING min_val IS NOT NULL
The new query after WHERE looks like:
WHERE
1 AND `city` = "madurai" AND `country` = "india"
HAVING min_val IS NOT NULL
Your query is overly complex and can be much simplified:
The two correlated sub queries are exactly the same, except for the SELECT list (MIN versus COUNT), so they could be combined into one;
The aggregation done by the sub query can be done in the main query;
The condition for checking availability can be written much shorter.
In fact, you can do all of what you need with the following query:
SELECT h.hotel_id,
COUNT(rm.room_id) as avail_room_count,
MIN(rm.room_prize) AS min_val
FROM abserve_hotels AS h
INNER JOIN abserve_hotel_rooms AS rm
ON rm.hotel_id = h.hotel_id
WHERE h.city = "madurai"
AND h.country = "india"
AND rm.adults_count >= 1
AND rm.room_count >= 1
AND rm.room_prize BETWEEN 174 AND 600
AND ( rm.check_in_time >= '2016-03-22'
OR rm.check_out_time <= '2016-03-15'
OR rm.check_in_time IS NULL)
GROUP BY h.hotel_id
Because the INNER JOIN requires at least one match, you can already be sure that min_val will never be NULL.
The check for availability is just as simple as:
( rm.check_in_time >= '2016-03-22'
OR rm.check_out_time <= '2016-03-15'
OR rm.check_in_time IS NULL)
The three parts of that condition mean:
The reservation for this room is future and does not overlap with this week;
The reservation for this room is in the past, the room is free today at the latest;
The room has no known reservation.
In all three cases the room is available for reservation for the concerned week.

getting most recent result - mysql, left join

I have 3 tables:
'art' contains a list of products ('art_id', 'art_nom' for name)
'mag' is the list of shops ('mag_id')
'pri' contains the prices of the products for a specific shop. There are several prices for one product, and even several prices for one shop, depending on the date ('pri_pri' is the price, 'pri_dat' is the date at which the price was registered and is on the datetime format).
I want the list of the products with 3 associated most recent prices (for the 3 shops selected). I got them with this request:
SELECT art.art_id, art_nom,
pri0.pri_pri AS pri_pri0,
pri1.pri_pri AS pri_pri1,
pri2.pri_pri AS pri_pri2
FROM (((art
LEFT JOIN (SELECT pri_pri, art_id, mag_id AS nbr_pri0 FROM pri WHERE mag_id = '7081' GROUP BY art_id) AS pri0 ON art.art_id = pri0.art_id)
LEFT JOIN (SELECT pri_pri, art_id, mag_id AS nbr_pri1 FROM pri WHERE mag_id = '14432' GROUP BY art_id) AS pri1 ON art.art_id = pri1.art_id)
LEFT JOIN (SELECT pri_pri, art_id, mag_id AS nbr_pri2 FROM pri WHERE mag_id = '14515' GROUP BY art_id) AS pri2 ON art.art_id = pri2.art_id)
I receive the list as I want, except that I don't have the most recent price.
So if somebody could help me adding a condition getting the most recent price, that would be really appreciated.
And I am really not sure if this way is very efficient. So if you have some advice on simplifying it...
Thanks !
note: i simplified my request so it is easier to read. If you want the complete request, with 5 prices and an 'ORDER BY' on the quantity of prices, here it is:
SELECT art.art_id, cat_id1, cat_id2, cat_id3, art_ean, art_nom, art_unt, art_qtt,
mrq_nom, mrq_img,
(IFNULL(nbr_pri0, 0) + IFNULL(nbr_pri1, 0) + IFNULL(nbr_pri2, 0) + IFNULL(nbr_pri3, 0) + IFNULL(nbr_pri4, 0)) AS nbr_pri,
nbr_pri0, pri0.pri_pri AS pri_pri0,
nbr_pri1, pri1.pri_pri AS pri_pri1,
nbr_pri2, pri2.pri_pri AS pri_pri2,
nbr_pri3, pri3.pri_pri AS pri_pri3,
nbr_pri4, pri4.pri_pri AS pri_pri4
FROM mrq, (((((art
LEFT JOIN (SELECT pri_pri, art_id, mag_id, COUNT(pri_id) AS nbr_pri0 FROM pri WHERE mag_id = '7081' GROUP BY art_id) AS pri0 ON art.art_id = pri0.art_id)
LEFT JOIN (SELECT pri_pri, art_id, mag_id, COUNT(pri_id) AS nbr_pri1 FROM pri WHERE mag_id = '14432' GROUP BY art_id) AS pri1 ON art.art_id = pri1.art_id)
LEFT JOIN (SELECT pri_pri, art_id, mag_id, COUNT(pri_id) AS nbr_pri2 FROM pri WHERE mag_id = '14515' GROUP BY art_id) AS pri2 ON art.art_id = pri2.art_id)
LEFT JOIN (SELECT pri_pri, art_id, mag_id, COUNT(pri_id) AS nbr_pri3 FROM pri WHERE mag_id = '12458' GROUP BY art_id) AS pri3 ON art.art_id = pri3.art_id)
LEFT JOIN (SELECT pri_pri, art_id, mag_id, COUNT(pri_id) AS nbr_pri4 FROM pri WHERE mag_id = '8136' GROUP BY art_id) AS pri4 ON art.art_id = pri4.art_id)
WHERE mrq.mrq_id = art.mrq_id
ORDER BY nbr_pri DESC
LIMIT 0,50
edit: here are the scripts for the tables as asked by Nitu and Strawberry (i hope so, i am not sure of what "DDLs" and "sqlfiddle" mean):
CREATE TABLE IF NOT EXISTS `art` (
`art_id` int(11) NOT NULL AUTO_INCREMENT,
`art_nom` char(40) NOT NULL,
PRIMARY KEY (`art_id`)
);
INSERT INTO `art` (`art_id`, `art_nom`) VALUES
(1, 'Coca-Cola classic'),
(2, 'Coca-Cola vanille'),
(3, 'Coca-Cola Cherry'),
(4, 'Coca-Cola Light lemon');
CREATE TABLE IF NOT EXISTS `mag` (
`mag_id` int(6) NOT NULL AUTO_INCREMENT,
`mag_vil` char(100) NOT NULL,
`mag_nom` char(100) NOT NULL,
PRIMARY KEY (`mag_id`)
);
INSERT INTO `mag` (`mag_id`, `mag_vil`, `mag_nom`) VALUES
(2, '01100 Oyonnax', 'Petit Casino Oyonnax'),
(3, '75001 Paris', 'Petit Casino Paris'),
(4, '69001 Lyon', 'Petit Casino Lyon');
CREATE TABLE IF NOT EXISTS `pri` (
`pri_id` int(11) NOT NULL AUTO_INCREMENT,
`art_id` int(11) NOT NULL,
`mag_id` int(6) NOT NULL,
`pri_pri` float NOT NULL,
`pri_dat` datetime NOT NULL,
PRIMARY KEY (`pri_id`)
);
INSERT INTO `pri` (`pri_id`, `art_id`, `mag_id`, `pri_pri`, `pri_dat`) VALUES
(1, 1, 14515, 2.61, '2014-08-12 17:28:48'),
(2, 1, 12458, 1.74, '2014-04-01 17:52:00'),
(3, 1, 12458, 2.39, '2014-03-15 17:52:00'),
(4, 3, 12458, 1.93, '2014-07-31 20:00:00'),
(5, 3, 8136, 1.21, '2014-08-08 14:02:00');
edit of the edit: for this data, I want to receive the price 1.74 (pri_id = 2) for the product art_id = 1, because the date is more recent than for the price 2.39 (pri_id = 3)
You can change your subqueries to something like
SELECT pri_date, MAX(pri_date) AS maxdate, pri_pri, art_id, mag_id, COUNT(pri_id) AS nbr_pri0
FROM pri
WHERE mag_id = '7081'
GROUP BY art_id
HAVING maxdate=pri_date
Which will give you the recent date.