I have a query that I want to optimize. Both unions executed separatley run fine, however as soon as I include inner join it takes up to 57 seconds. How do I solve this. My query is as follows
SELECT
p.PROJID,
p.StartDate,
o.ORDERNO,
p.PROJCODE,
p.PROJECT,
cat.type AS CATEGORY,
p.AREA,
p.STATE,
p.COUNTRY,
p.VALUE,
p.PROCESSOR,
p.PROJINFO,
p.NES,
p.SPECSALE,
p.OFFICE,
p.DEPTCODE,
p.INTERNLCHG,
p.INTERCOCHG,
p.LORM,
p.PERCENT,
d.COMPANY,
CONCAT(
d.LASTNAME,
", ",
d.FIRSTNAME
) AS Contact
FROM
(
(
SELECT
*
FROM
(
SELECT
`clients`.`CLIENTID` AS `CLIENTIDA`,
`clients`.`COMPANY` AS `COMPANY`
FROM
`hdb`.`clients`
UNION
SELECT
`accounts`.`id` AS `CLIENTIDA`,
`accounts`.`name` AS `COMPANY`
FROM
`sugarcrm`.`accounts`
) AS a
INNER JOIN (
SELECT
`hdb`.`contacts`.`CONTACTID` AS `CONTACTID`,
`hdb`.`contacts`.`CLIENTID` AS `CLIENTIDC`,
`hdb`.`contacts`.`FIRSTNAME` AS `FIRSTNAME`,
`hdb`.`contacts`.`LASTNAME` AS `LASTNAME`
FROM
`hdb`.`contacts`
UNION
SELECT
`sugarcrm`.`contacts`.`id` AS `CONTACTID`,
`sugarcrm`.`accounts_contacts`.`account_id` AS `CLIENTIDC`,
`sugarcrm`.`contacts`.`first_name` AS `FIRSTNAME`,
`sugarcrm`.`contacts`.`last_name` AS `LASTNAME`
FROM
`sugarcrm`.`contacts`
LEFT JOIN `sugarcrm`.`accounts_contacts` ON `sugarcrm`.`contacts`.`id` = `sugarcrm`.`accounts_contacts`.`contact_id`
) AS c ON a.CLIENTIDA = c.CLIENTIDC
) AS d
)
INNER JOIN (
(
projects AS p
INNER JOIN orders AS o ON p.ORDERNO = o.ORDERNO
)
INNER JOIN category AS cat ON p.category_id = cat.category_id
) ON d.CONTACTID = o.CONTACTID
Explain on this provides the following:
1, PRIMARY, cat, ALL, PRIMARY, , , , 10,
1, PRIMARY, p, ref, FK_orders_projects,FK_category_projects_idx, FK_category_projects_idx, 5, hdb.cat.category_id, 400, Using where
1, PRIMARY, o, eq_ref, PRIMARY, PRIMARY, 4, hdb.p.ORDERNO, 1,
1, PRIMARY, <derived2>, ALL, , , , , 18878, Using where
2, DERIVED, <derived3>, ALL, , , , , 7087,
2, DERIVED, <derived5>, ALL, , , , , 18879, Using where
5, DERIVED, contacts, ALL, , , , , 8261,
6, UNION, contacts, ALL, , , , , 10251,
6, UNION, accounts_contacts, ref, idx_contid_del_accid, idx_contid_del_accid, 111, sugarcrm.contacts.id, 1, Using index
, UNION RESULT, <union5,6>, ALL, , , , , ,
3, DERIVED, clients, ALL, , , , , 2296,
4, UNION, accounts, ALL, , , , , 4548,
, UNION RESULT, <union3,4>, ALL, , , , , ,
The original query without the union takes 0.125 seconds
SELECT p.PROJID, p.StartDate, o.ORDERNO, p.PROJCODE, p.PROJECT, cat.type AS CATEGORY, p.AREA, p.STATE, p.COUNTRY,
p.VALUE, p.PROCESSOR, p.PROJINFO, p.NES, p.SPECSALE, p.OFFICE, p.DEPTCODE, p.INTERNLCHG, p.INTERCOCHG, p.LORM,
p.PERCENT, a.COMPANY, CONCAT(c.LASTNAME, ", ", c.FIRSTNAME) AS Contact
FROM (clients AS a
INNER JOIN contacts AS c ON a.CLIENTID =c.CLIENTID)
INNER JOIN ((projects AS p INNER JOIN orders AS o ON p.ORDERNO = o.ORDERNO)
INNER JOIN category AS cat ON p.category_id = cat.category_id) ON c.CONTACTID = o.CONTACTID
ORDER BY p.PROJID, a.COMPANY;
explain on this provides following:
1, SIMPLE, cat, ALL, PRIMARY, , , , 10, Using temporary; Using filesort
1, SIMPLE, p, ref, FK_orders_projects,FK_category_projects_idx, FK_category_projects_idx, 5, hdb.cat.category_id, 400, Using where
1, SIMPLE, o, eq_ref, PRIMARY,FK_contacts_orders, PRIMARY, 4, hdb.p.ORDERNO, 1,
1, SIMPLE, c, eq_ref, PRIMARY,FK_clients_contacts, PRIMARY, 52, hdb.o.CONTACTID, 1,
1, SIMPLE, a, eq_ref, PRIMARY, PRIMARY, 52, hdb.c.CLIENTID, 1,
Query with view:
SELECT
p.PROJID,
p.StartDate,
o.ORDERNO,
p.PROJCODE,
p.PROJECT,
cat.type AS CATEGORY,
p.AREA,
p.STATE,
p.COUNTRY,
p.VALUE,
p.PROCESSOR,
p.PROJINFO,
p.NES,
p.SPECSALE,
p.OFFICE,
p.DEPTCODE,
p.INTERNLCHG,
p.INTERCOCHG,
p.LORM,
p.PERCENT,
a.COMPANY,
CONCAT(
c.LASTNAME,
", ",
c.FIRSTNAME
) AS Contact
FROM
(
view_accounts_sugar_hdb AS a
INNER JOIN view_contacts_sugar_hdb AS c ON a.CLIENTID = c.CLIENTID
)
INNER JOIN (
(
projects AS p
INNER JOIN orders AS o ON p.ORDERNO = o.ORDERNO
)
INNER JOIN category AS cat ON p.category_id = cat.category_id
) ON c.CONTACTID = o.CONTACTID
ORDER BY
p.PROJID,
a.COMPANY;
This takes over 340 secs.
This one was definitely uglier than the last one I helped you on :)... Anyhow, the same principles apply. For future, please do try to understand what I'm doing here. Write the JOIN relationships FIRST to know where your data is coming from. Also, take a look at my indentations... I am showing at each level for readability.
Orders -> Projects -> Categories...
then, the path for those in the normal clients table via
Orders -> Contacts -> Clients
finally to the SugarCRM contacts...
Orders -> Accounts_Contacts -> Accounts
So, now that you have the relationships set (and aliased), this follows similar of last answer implementing LEFT-JOIN to normal contact/client vs CRM Contacts/Accounts.
The field list is simple on the Order, Products and Category tables as those are pretty direct. That leaves just the "who/client" information where the LEFT-JOIN comes in. If the Normal client is null, use the CRM version fields, otherwise use the normal client fields...
SELECT
P.PROJID,
P.StartDate,
O.ORDERNO,
P.PROJCODE,
P.PROJECT,
cat.`type` AS CATEGORY,
P.AREA,
P.STATE,
P.COUNTRY,
P.VALUE,
P.PROCESSOR,
P.PROJINFO,
P.NES,
P.SPECSALE,
P.OFFICE,
P.DEPTCODE,
P.INTERNLCHG,
P.INTERCOCHG,
P.LORM,
P.PERCENT,
CASE when HCLIENT.ClientID IS NULL
then SCLIENT.`name`
ELSE HCLIENT.Company end as Company,
CASE when HCLIENT.ClientID IS NULL
then CONCAT( SCT.LAST_NAME, ", ", SCT.FIRST_NAME )
ELSE CONCAT( HCT.LASTNAME, ", ", HCT.FIRSTNAME ) end as Contact
FROM
orders O
JOIN projects P
ON O.OrderNo = P.OrderNo
JOIN category AS cat
ON p.category_id = cat.category_id
LEFT JOIN hdb.contacts HCT
ON O.ContactID = HCT.ContactID
LEFT JOIN hdb.clients HCLIENT
ON HCT.ClientID = HCLIENT.ClientID
LEFT JOIN sugarcrm.contacts SCT
ON O.ContactID = SCT.ID
LEFT JOIN sugarcrm.accounts_contacts SAC
ON SCT.ID = SAC.contact_id
LEFT JOIN sugarcrm.accounts SCLIENT
ON SCT.account_id = SCLIENT.ID
I'd be interested in the performance improvement too.
Related
I want to insert two different tables into one as per the below illustration:
I've tried several ways like:
INSERT INTO com_short
(category_id, media_folder_id, gallery_folder_id, status, important,
bgPositioning, published_at, published_by, created_at, modified_at,title,
slug, body, featured_image_file_id)
SELECT (SELECT com_post.id, category_id, media_folder_id, gallery_folder_id,
status, important, bgPositioning, published_at, published_by,
com_post.created_at, com_post.modified_at
FROM com_post
INNER JOIN com_post_translation ON com_post.id = com_post_translation.post_id
WHERE com_post_translation.post_id = com_post.id
),
(SELECT title, slug, body,featured_image_file_id
FROM com_post_translation
WHERE com_post.id = com_post_translation.post_id
);
But it shows an error as follows:
What am I missing?
Reason of the error: you can not select more than one column in the subquery:
SELECT (SELECT com_post.id, category_id, media_folder_id, gallery_folder_id,
status, important, bgPositioning, published_at, published_by,
com_post.created_at, com_post.modified_at
FROM com_post
INNER JOIN com_post_translation ON com_post.id = com_post_translation.post_id
WHERE com_post_translation.post_id = com_post.id
),
(SELECT title, slug, body,featured_image_file_id
FROM com_post_translation
WHERE com_post.id = com_post_translation.post_id
);
Simple explanation :
This will produce error:
select (select '1', '2');
This will not:
select (select '1');
Check the demo
What would be the way to do it withot errors:
INSERT INTO com_short (category_id --1
, media_folder_id --2
, gallery_folder_id --3
, status --4
, important --5
, bgPositioning --6
, published_at --7
, published_by --8
, created_at --9
, modified_at --10
, title --11
, slug --12
, body --13
, featured_image_file_id) --14
SELECT cpt.category_id --1
, cpt.media_folder_id --2
, cpt.gallery_folder_id --3
, cpt.status --4
, cpt.important --5
, cpt.bgPositioning --6
, cpt.published_at --7
, cpt.published_by --8
, com_post.created_at --9
, com_post.modified_at --10
, cpt.title --11
, cpt.slug --12
, cpt.body --13
, cpt.featured_image_file_id --14
FROM com_post
INNER JOIN com_post_translation cpt ON com_post.id = cpt .post_id
Note: This is just an example, I can not know for sure from your question what do you want to do....
Additional info:
cpt is alias for the table com_post_translation (It makes your code "lighter" and easyer to read).
When you join two tables with ON keyword there is no need to repeat that condition in the where clause.
For the table:
assistants_rating(
id INT PRIMARY KEY auto_increment,
assistant_id INT(11),
rating INT(1)
)
And another table of assistants which contains a name. My query goes as follows:
(SELECT assistants.name AS assist_name , count(rating) AS OneStar FROM assistants_rating
INNER JOIN assistants on assistants.assistant_id = assistants_rating.assistant_id WHERE rating = 1)
UNION
(SELECT assistants.name AS assist_name , count(rating) AS TwoStar FROM assistants_rating
INNER JOIN assistants on assistants.assistant_id = assistants_rating.assistant_id WHERE rating = 2)
UNION
(SELECT assistants.name AS assist_name , count(rating) AS ThreeStar FROM assistants_rating
INNER JOIN assistants on assistants.assistant_id = assistants_rating.assistant_id WHERE rating = 3)
GROUP BY assistants.name,OneStar,TwoStar,ThreeStar;
Let's say I have the assistants named Bob and Sophie, the query should return:
assist_name OneStar TwoStar ThreeStar
Bob 9 18 52
Sophie 15 8 61
But instead I'm getting a SQL syntax error, which is weird because when I do each of them alone , they work properly and that leads me to believe that my syntax is good. Not only that, but my queries with the UNION work if I only do TwoStar and ThreeStar together but not with OneStar and TwoStar.
I'm so confused; why doesn't this work?
UNION doesn't help here anyway. It does a different thing.
The query you need is something along the lines of:
SELECT
assistants.name AS assist_name,
SUM(IF(rating = 1, 1, 0)) AS OneStar,
SUM(IF(rating = 2, 1, 0)) AS TwoStar,
SUM(IF(rating = 3, 1, 0)) AS ThreeStar
FROM assistants_rating
INNER JOIN assistants ON assistants.assistant_id = assistants_rating.assistant_id
GROUP BY assistants.name
You can use CASE instead of Union
SELECT assistants.name AS assist_name,
SUM(CASE WHEN rating =1 THEN 1 ELSE 0 END) AS OneStar,
SUM(CASE WHEN rating =2 THEN 1 ELSE 0 END) AS TwoStar,
SUM(CASE WHEN rating =3 THEN 1 ELSE 0 END) AS ThreeStar
FROM assistants_rating
INNER JOIN assistants on assistants.assistant_id = assistants_rating.assistant_id
GROUP BY assistants.name
I am trying to create a table the shows a booking summary for a rafting business. The EER diagram shows how everything is related. I am struggling with how to show only 1 row per trip (i am returning multiple rows and the numbers are multiplied). Here is my code so far.
use www;
SELECT
d.destination_name,
tt.trip_type_name,
t.trip_number,
t.trip_date,
(e.first_name + e.last_name) AS guide_name,
t.trip_capacity,
COUNT(r.guest_id) AS guests_booked,
(COUNT(r.guest_id)-t.trip_capacity) AS positions_available
FROM
employees e,
destination d,
trip_type tt,
trips t,
reservation r
GROUP BY d.destination_name , tt.trip_type_name , t.trip_number , t.trip_date , guide_name , t.trip_capacity
ORDER BY d.destination_name , tt.trip_type_name , t.trip_date , t.trip_number;
OK so I have figured out the JOINs I am now struggling with concatenating the GUIDE NAME, it shows up as the number 0(zero). I have the same issue with another table that needs to show the guest name from a concatenated first and last name that corresponds to an id and trip number.
here is my code:
use www;
SELECT
d.destination_name,
tt.trip_type_name,
t.trip_number,
t.trip_date,
t.trip_capacity,
CONCAT(e.nick_name+' '+e.last_name AS guide_name
COUNT(r.guest_id) AS guests_booked,
(t.trip_capacity - COUNT(r.guest_id)) AS positions_available
FROM
trip_type tt
JOIN
trips t ON tt.trip_type_code = t.trip_type_code
JOIN
destination d ON t.destination_code = d.destination_code
JOIN
reservation r ON t.trip_number = r.trip_number
GROUP BY trip_number;
Second SQL query
use www;
SELECT
d.destination_name,
tt.trip_type_name,
t.trip_number,
t.trip_date,
CONCAT(e.last_name + ', ' + e.first_name) AS guide_name,
CONCAT(g.last_name + ', ' + g.first_name) AS guest_name,
ex.exp_name AS guest_experience,
g.age AS guest_age,
g.weight AS guest_weight,
g.swimmer AS guest_is_swimmer,
g.mobile_phone AS guest_mobile_phone
FROM
trip_type tt
JOIN
trips t ON tt.trip_type_code = t.trip_type_code
JOIN
destination d ON t.destination_code = d.destination_code
JOIN
reservation r ON t.trip_number = r.trip_number
JOIN
guests g ON r.guest_id = g.guest_id
JOIN
experience ex ON ex.exp_code = g.exp_code
JOIN
employees e ON t.guide_employee_id = e.employee_id
ORDER BY d.destination_name , tt.trip_type_name , t.trip_date , g.last_name , e.employee_id
You need condition on relation otherwise you obtain a cartesian product between the tables .
SELECT
d.destination_name,
tt.trip_type_name,
t.trip_number,
t.trip_date,
(e.first_name + e.last_name) AS guide_name,
t.trip_capacity,
COUNT(r.guest_id) AS guests_booked,
(COUNT(r.guest_id)-t.trip_capacity) AS positions_available
FROM trips t
inner join employees e on e.employee_id = t.employee_employee_id
inner join destination d on d.destination_code = t.destination_code
inner join trip_type tt on tt.trip_type_cde = t.trip_type_cde
inner join reservation r on r.trip_number = t.reservation_trip_number
GROUP BY d.destination_name , tt.trip_type_name , t.trip_number , t.trip_date , guide_name , t.trip_capacity
ORDER BY d.destination_name , tt.trip_type_name , t.trip_date , t.trip_number;
I am working on a game inventory management system and would like to display the owner's restock wish list and a count of customer buy reservations for each game in a single table. I wrote a query that I thought was working, but then I noticed that it actually omits any games for which there are reservations but that aren't initially in the restock wish list. The query is below:
SELECT rwl.*, g.gameName, coalesce(payYes, 0) payYes, coalesce(payNo, 0) payNo FROM RestockWishList AS rwl, Games AS g
LEFT JOIN
(SELECT gameID, COUNT(if(prepaid='Yes', 1, NULL)) payYes, COUNT(if(prepaid='No', 1, NULL)) payNo FROM ReservationsBuy GROUP BY gameID) AS res
ON res.gameID = g.gameID
WHERE rwl.gameID = g.gameID;
Query results:
gameID,
quantity,
gameName,
payYes,
payNo
1,
4,
A Castle for all Seasons,
0,
0
2,
2,
A Few Acres of Snow,
0,
0
18,
4,
Alhambra,
0,
0
54,
2,
Big Boggle,
2,
0
Apparently the solution to this problem is to use FULL OUTER JOIN instead of LEFT JOIN, but MySQL doesn't support that function. I have spent hours trying to translate it to a UNION structure, but can't quite get it to work correctly. This is as close as I've got:
SELECT rwl.*, res.gameID, res.payYes, res.payNo FROM RestockWishList rwl
LEFT JOIN
(SELECT gameID, COUNT(if(prepaid='Yes', 1, NULL)) payYes, COUNT(if(prepaid='No', 1, NULL)) payNo FROM ReservationsBuy GROUP BY gameID) AS res
ON res.gameID = rwl.gameID
UNION
SELECT rwl.*, res.gameID, COUNT(if(prepaid='Yes', 1, NULL)) payYes, COUNT(if(prepaid='No', 1, NULL)) payNo FROM ReservationsBuy res
LEFT JOIN RestockWishList rwl ON rwl.gameID = res.gameID;
Query results:
gameID, quantity, gameID, payYes, payNo
1, 4, NULL, NULL, NULL
2, 2, NULL, NULL, NULL
18, 4, NULL, NULL, NULL
54, 2, 54, 2, 0
NULL, NULL, 30, 3, 1
(Sorry, I don't know how to nicely format query table results in StackOverflow.)
I want the query to display as I originally wrote it, just with the missing values from ReservationsBuy. Specific help please?
Tables:
CREATE TABLE IF NOT EXISTS RestockWishList (
gameID INT(6),
quantity INT(3) NOT NULL,
PRIMARY KEY (gameID),
FOREIGN KEY (gameID) REFERENCES Games(gameID) ON UPDATE CASCADE ON DELETE CASCADE);
CREATE TABLE IF NOT EXISTS ReservationsBuy (
gameID INT(6),
customerEmail VARCHAR(25) NOT NULL,
customerName VARCHAR(25) NOT NULL,
dateReserved DATETIME NOT NULL, #date customer files game reservation
datePurchased DATETIME, #date Board and Brew restocks game
dateClaimed DATETIME, #date customer physically claims game
prepaid ENUM('Yes', 'No') NOT NULL,
PRIMARY KEY (gameID, customerEmail),
FOREIGN KEY (gameID) REFERENCES Games (gameID) ON UPDATE CASCADE ON DELETE CASCADE);
Sample data:
RestockWishList:
gameID,
quantity
1,
4
2,
2
18,
4
54,
2
ReservationsBuy:
gameID,
customerEmail,
customerName,
dateReserved,
datePurchased,
dateClaimed,
prepaid
30,
wonder#woman.com,
Diana,
2015-04-24 14:46:05,
NULL,
NULL,
Yes
54,
boggie#marsh.com,
boggie,
2015-04-24 14:43:32,
NULL,
NULL,
Yes
54,
manny#second.com,
manny,
2015-04-27 19:48:22,
NULL,
NULL,
Yes
43,
old#mom.com,
grandma,
2015-04-23 22:32:03,
NULL,
NULL,
No
Expected output:
gameID, quantity, gameName, payYes, payNo
1, 4, A Castle for all Seasons, 0, 0
2, 2, A Few Acres of Snow, 0, 0
18, 4, Alhambra, 0, 0
30, 0, Arkham Horror, 1, 0
43, 0, Bananagrams, 0, 1
54, 2, Big Boggle, 2, 0
(Games table not particularly important for this query. Only relevance is that both ReservationsBuy and RestockWishList are connected to Games by gameID)
I think maybe you want a query like this - not a full outer join:
select q.id, q.name, q.reservations, ifnull(q2.wishcount, 0) wishcount, q.payYes, q.payNo
from (
select g.*, count(rb.gameid) reservations, count(case when prepaid = 'Yes' then 1 end) payYes, count(case when prepaid = 'No' then 1 end) payNo
from games g
left join reservationsbuy rb
on g.id = rb.gameid
group by g.id
) q
left join (
select g.id, sum(quantity) wishcount
from games g
left join restockwishlist rwl
on g.id = rwl.gameid
group by g.id
) q2
on q.id = q2.id;
There's a demo here, but the gist of it is, for each game in the game table, it will give you the total number of reservations, the quantity from the wish list, and we use a conditional count to provide the count ofprepaid = yes, or prepaid = no. Effectively it is just joining together two small queries on the shared gameid.
If you want this to include filtering by date etc, you might need to be a bit more explicit about how you want the results to work, or display
You're on the right track with using a FULL OUTER JOIN, you just have the implementation incorrect.
A FULL OUTER JOIN in MySQL can be thought of as the UNION of a LEFT JOIN and a RIGHT JOIN. In your query, you're trying to approximate this by treating the RIGHT JOIN part of the logic as an inverse LEFT JOIN of the two tables, but your first part doesn't work because it's not a subselect with the same GROUP BY sequence as your first LEFT JOIN.
Simplest thing to do is simply take your first LEFT JOIN query stanza, copy it to the second stanza, and replace LEFT JOIN with RIGHT JOIN, then link the results to your games table, like so:
SELECT g.gameID, IFNULL(q.quantity, 0) AS quantity, g.gameName,
IFNULL(q.payYes, 0) AS payYes, IFNULL(q.payNo, 0) AS payNo
FROM games g
INNER JOIN (
SELECT IFNULL(rwl.gameID, res.gameID) AS gameID, rwl.quantity,
res.payYes, res.payNo
FROM RestockWishList rwl
LEFT JOIN (
SELECT gameID, COUNT(if(prepaid='Yes', 1, NULL)) payYes,
COUNT(if(prepaid='No', 1, NULL)) payNo
FROM ReservationsBuy
GROUP BY gameID
) AS res ON res.gameID = rwl.gameID
UNION
SELECT IFNULL(rwl.gameID, res.gameID) AS gameID, rwl.quantity,
res.payYes, res.payNo
FROM RestockWishList rwl
RIGHT JOIN (
SELECT gameID, COUNT(IF(prepaid='Yes', 1, NULL)) payYes,
COUNT(IF(prepaid='No', 1, NULL)) payNo
FROM ReservationsBuy
GROUP BY gameId
) AS res ON res.gameID = rwl.gameID
) AS q ON g.gameID = q.gameID
SQL Fiddle Results
ok. So we now that always exists a record in game table, right?
Then start your FROM with this table, then you just have to do a LEFT JOIN for each table as follow:
SELECT
rwl.*
, g.gameName
, coalesce(payYes, 0) payYes
, coalesce(payNo, 0) payNo
FROM
Games AS g LEFT JOIN
RestockWishList AS rwl ON rwl.gameID = g.gameID LEFT JOIN
(
SELECT
gameID
, COUNT(if(prepaid='Yes', 1, NULL)) payYes
, COUNT(if(prepaid='No', 1, NULL)) payNo
FROM
ReservationsBuy
GROUP BY gameID) AS res ON res.gameID = g.gameID
;
As you can say, the only changes were: Start the FROM with Games table and use LEFT JOIN, also remove the condition from WHERE and put it in the LEFT JOIN
I'm struggling with an problem to make a query of the following:
I have an table called Article_Statuses (and the main table Articles, with ArticleID as p.key) and has the following structure
ID, ArticleID, Status, Status_Date, Category
In this table I collect all the statuses of the articles (ArticleID) per category (which are predefined), the highest ID per category is the latest status of that category, herewith some data:
1, BB0001, LFS, 15-01-2015, LIC
2, BB0001, LFA, 19-01-2015, LIC
3, BB0001, SA, 10-01-2015, FIS
4, BB0001, CA, 19-01-2015, FIS
5, BB0002, LFS, 10-01-2015, LIC
6, BB0002, LFA, 11-01-2015, LIC
7, BB0003, CA, 19-01-2015, FIS
I want to make a query with the following result:
ArticleID, Status LIC, Status_Date LIC, Status FIS, Status_Date FIS
BB0001, LFA, 19-01-2015, CA, 19-01-2015
BB0002, LFA, 11-01-2015, ,
BB0003, , , CA, 19-01-2015
I found the following solution which works for only one category, I'm stuck with adding the other categories...
SELECT `a`.`ArticleID`, `b`.`Status_Date` AS `LIC_Date`, `b`.`Status` AS `LIC_Status`
FROM `Articles` `a`
INNER JOIN `Article_Statuses` `b` ON `a`.`ArticleID` = `b`.`ArticleID`
INNER JOIN ( SELECT `ArticleID`, MAX( `ID` ) `MAXID`
FROM `Article_Statuses`
WHERE `Category` = 'LIC' GROUP BY `ArticleID` ) `c`
ON `b`.`ArticleID` = `c`.`ArticleID` AND `b`.`ID` = `c`.`MAXID`
WHERE `a`.`Partner` = 10
GROUP BY `a`.`ArticleID`
ORDER BY `a`.`ArticleID` ASC
What is the meaning of "Partner" in your query? You didn't mentioned it anywhere earlier so I'm guessing it's not important.
How many different categories do you have? Just two or more? I'm asking because returning data in such a way is far for being fast and optimal.
It would be something like:
SELECT
a.ArticleID,
b.Status_Date AS LIC_Date, b.Status AS LIC_Status,
d.Status_Date AS FIS_Date, d.Status AS FIS_Status
FROM Articles AS a
INNER JOIN Article_Statuses AS b ON a.ArticleID = b.ArticleID
INNER JOIN (
SELECT ArticleID, MAX( ID ) AS ID
FROM Article_Statuses
WHERE Category = 'LIC' GROUP BY ArticleID
) AS c ON b.ArticleID = c.ArticleID AND b.ID = c.ID
INNER JOIN Article_Statuses AS d ON a.ArticleID = d.ArticleID
INNER JOIN (
SELECT ArticleID, MAX( ID ) AS ID
FROM Article_Statuses
WHERE Category = 'FIS' GROUP BY ArticleID
) AS e ON d.ArticleID = e.ArticleID AND d.ID = e.ID
WHERE a.Partner = 10
ORDER BY a.ArticleID ASC
Basically you repeat joins just with different aliased - I used "d" and "e" but it's better to use something meaningful like "LIC_category" instead of just "b".
You also should use left join instead of inner join if some categories can be empty as in your example.
This is the query which returns desired result:
select a.articleid,
b.status_date as LIC_Date, b.status as LIC_Status,
c.status_date as FIS_Date, c.status as FIS_Status
from Articles a
left join Article_Statuses b on b.ArticleID =a.articleid and
b.id = (select max(id) from Article_Statuses ast
where ast.articleid=a.articleid and ast.category='LIC')
left join Article_Statuses c on c.ArticleID =a.articleid and
c.id = (select max(id) from Article_Statuses ast
where ast.articleid=c.articleid and ast.category='FIS')
order by a.articleid
I used left joins because not for every article both categories are present.