Resulting data on last thread_date - sql

As I fixed the previous question thanks to the answer, I am sticking now in the part to retrieve a thread based on the last thread_date.
The code itself seems to be working fine, but it is only printing one result out instead of others.
The thread has a threads.cat_id which is linked to thesubsubcategory.extra_cat_id.
SELECT
parent.subcat_id,
parent.subcat_name,
child.subsubcat_name,
child.subcat_id,
child.cat_id,
kid.thread_name,
kid.cat_id,
kid.thread_date
FROM
subcategories parent
JOIN subsubcategories child
ON child.cat_id = parent.cat_id
JOIN threads kid ON child.extra_cat_id = kid.cat_id
WHERE thread_date = (SELECT MAX(thread_date) FROM threads)
What I am expecting is this:
Category
Subcategory Latest thread
Subcategory Latest thread
What I am getting is this:
Category
Subcategory Latest thread
SQL fiddle: http://sqlfiddle.com/#!9/52e27/2
Any solutions to it?
Thanks!

If my guess is right, you want to edit the where clause to show more based on last date as your threaddate is a datetime, you have to convert it to a date format before comparing it.
SELECT parent.subcat_id,
parent.subcat_name,
child.subsubcat_name,
child.subcat_id,
child.cat_id,
kid.thread_name,
kid.cat_id,
kid.thread_date
FROM subcategories parent
INNER JOIN subsubcategories child
ON child.cat_id = parent.cat_id
INNER JOIN threads kid
ON child.extra_cat_id = kid.cat_id
WHERE convert(date,thread_date,108) = (
SELECT MAX(convert(date,thread_date,108))
FROM threads
)

Do you need to get a thread with maximal date for every subsubcategory, don't you?
SELECT
parent.subcat_id,
parent.subcat_name,
child.subsubcat_name,
child.subcat_id,
child.cat_id,
kid.thread_name,
kid.cat_id,
kid.thread_date
FROM subcategories parent
JOIN subsubcategories child ON child.cat_id=parent.cat_id
JOIN threads kid ON kid.cat_id=child.extra_cat_id
WHERE kid.thread_date=
(SELECT MAX(kid2.thread_date)
FROM threads kid2
WHERE kid2.cat_id=child.extra_cat_id)

I got this fixed by creating a PHP function to return the latest thread with a
SELECT * FROM threads WHERE subcat_id = variable SQL.

Related

SQL - join three tables based on (different) latest dates in two of them

Using Oracle SQL Developer, I have three tables with some common data that I need to join.
Appreciate any help on this!
Please refer to https://i.stack.imgur.com/f37Jh.png for the input and desired output (table formatting doesn't work on all tables).
These tables are made up in order to anonymize them, and in reality contain other data with millions of entries, but you could think of them as representing:
Product = Main product categories in a grocery store.
Subproduct = Subcategory products to the above. Each time the table is updated, the main product category may loses or get some new suproducts assigned to it. E.g. you can see that from May to June the Pulled pork entered while the Fishsoup was thrown out.
Issues = Status of the products, for example an apple is bad if it has brown spots on it..
What I need to find is: for each P_NAME, find the latest updated set of subproducts (SP_ID and SP_NAME), and append that information with the latest updated issue status (STATUS_FLAG).
Please note that each main product category gets its set of subproducts updated at individual occasions i.e. 1234 and 5678 might be "latest updated" on different dates.
I have tried multiple queries but failed each time. I am using combos of SELECT, LEFT OUTER JOIN, JOIN, MAX and GROUP BY.
Latest attempt, which gives me the combo of the first two tables, but missing the third:
SELECT
PRODUCT.P_NAME,
SUBPRODUCT.SP_PRODUCT_ID, SUBPRODUCT.SP_NAME, SUBPRODUCT.SP_ID, SUPPRODUCT.SP_VALUE_DATE
FROM SUBPRODUCT
LEFT OUTER JOIN PRODUCT ON PRODUCT.P_ID = SUBPRODUCT.SP_PRODUCT_ID
JOIN(SELECT SP_PRODUCT_ID, MAX(SP_VALUE_DATE) AS latestdate FROM SUBPRODUCT GROUP BY SP_PRODUCT_ID) sub ON
sub.SP_PRODUCT_ID = SUBPRODUCT.SP_PRODUCT_ID AND sub.latestDate = SUBPRODUCT.SP_VALUE_DATE;
Trying to find a row with a max value is a common SQL pattern - you can do it with a join, like your example, but it's usually more clear to use a subquery or a window function.
Correlated subquery example
select
PRODUCT.P_NAME,
SUBPRODUCT.SP_PRODUCT_ID, SUBPRODUCT.SP_NAME, SUBPRODUCT.SP_ID, SUPPRODUCT.SP_VALUE_DATE,
ISSUES.STATUS_FLAG, ISSUES.STATUS_LAST_UPDATED
from PRODUCT
join SUBPRODUCT
on PRODUCT.P_ID = SUBPRODUCT.SP_PRODUCT_ID
and SUBPRODUCT.SP_VALUE_DATE = (select max(S2.SP_VALUE_DATE) as latestDate
from SUBPRODUCT S2
where S2.SP_PRODUCT_ID = SUBPRODUCT.SP_PRODUCT_ID)
join ISSUES
on ISSUES.ISSUE_ID = SUBPRODUCT.SP_ID
and ISSUES.STATUS_LAST_UPDATED = (select max(I2.STATUS_LAST_UPDATED) as latestDate
from ISSUES I2
where I2.ISSUE_ID = ISSUES.ISSUE_ID)
Window function / inline view example
select
PRODUCT.P_NAME,
S.SP_PRODUCT_ID, S.SP_NAME, S.SP_ID, S.SP_VALUE_DATE,
I.STATUS_FLAG, I.STATUS_LAST_UPDATED
from PRODUCT
join (select SUBPRODUCT.*,
max(SP_VALUE_DATE) over (partition by SP_PRODUCT_ID) as latestDate
from SUBPRODUCT) S
on PRODUCT.P_ID = S.SP_PRODUCT_ID
and S.SP_VALUE_DATE = S.latestDate
join (select ISSUES.*,
max(STATUS_LAST_UPDATED) over (partition by ISSUE_ID) as latestDate
from ISSUES) I
on I.ISSUE_ID = S.SP_ID
and I.STATUS_LAST_UPDATED = I.latestDate
This often performs a bit better, but window functions can be tricky to understand.

how to join multiple tables without showing repeated data?

I pop into a problem recently, and Im sure its because of how I Join them.
this is my code:
select LP_Pending_Info.Service_Order,
LP_Pending_Info.Pending_Days,
LP_Pending_Info.Service_Type,
LP_Pending_Info.ASC_Code,
LP_Pending_Info.Model,
LP_Pending_Info.IN_OUT_WTY,
LP_Part_Codes.PartCode,
LP_PS_Codes.PS,
LP_Confirmation_Codes.SO_NO,
LP_Pending_Info.Engineer_Code
from LP_Pending_Info
join LP_Part_Codes
on LP_Pending_Info.Service_order = LP_Part_Codes.Service_order
join LP_PS_Codes
on LP_Pending_Info.Service_Order = LP_PS_Codes.Service_Order
join LP_Confirmation_Codes
on LP_Pending_Info.Service_Order = LP_Confirmation_Codes.Service_Order
order by LP_Pending_Info.Service_order, LP_Part_Codes.PartCode;
For every service order I have 5 part code maximum.
If the service order have only one value it show the result correctly but when it have more than one Part code the problem begin.
for example: this service order"4182134076" has only 2 part code, first'GH81-13601A' and second 'GH96-09938A' so it should show the data 2 time but it repeat it for 8 time. what seems to be the problem?
If your records were exactly the same the distinct keyword would have solved it.
However in rows 2 and 3 which have the same Service_Order and Part_Code if you check the SO_NO you see it is different - that is why distinct won't work here - the rows are not identical.
I say you have some problem in one of the conditions in your joins. The different data is in the SO_NO column so check the raw data in the LP_Confirmation_Codes table for that Service_Order:
select * from LP_Confirmation_Codes where Service_Order = 4182134076
I assume you are missing an and with the value from the LP_Part_Codes or LP_PS_Codes (but can't be sure without seeing those tables and data myself).
By this sentence If the service order have only one value it show the result correctly but when it have more than one Part code the problem begin. - probably you are missing and and with the LP_Part_Codes table
Based on your output result, here are the following data that caused multiple output.
Service Order: 4182134076 has :
2 PartCode which are GH81-13601A and GH96-09938A
2 PS which are U and P
2 SO_NO which are 1.00024e+09 and 1.00022e+09
Therefore 2^3 returns 8 rows. I believe that you need to check where you should join your tables.
Use DINTINCT
select distinct LP_Pending_Info.Service_Order,LP_Pending_Info.Pending_Days,
LP_Pending_Info.Service_Type,LP_Pending_Info.ASC_Code,LP_Pending_Info.Model,
LP_Pending_Info.IN_OUT_WTY, LP_Part_Codes.PartCode,LP_PS_Codes.PS,
LP_Confirmation_Codes.SO_NO,LP_Pending_Info.Engineer_Code
from LP_Pending_Info
join LP_Part_Codes on LP_Pending_Info.Service_order = LP_Part_Codes.Service_order
join LP_PS_Codes on LP_Part_Codes.Service_Order = LP_PS_Codes.Service_Order
join LP_Confirmation_Codes on LP_PS_Codes.Service_Order = LP_Confirmation_Codes.Service_Order
order by LP_Pending_Info.Service_order, LP_Part_Codes.PartCode;
distinct will not return duplicates based on your select. So if a row is same, it will only return once.

SELECT Query for a Bill of Materials

Our Bill of Materials table has 3 main columns: Part Number, Parent Part, and Level Code (0 being no parent, 3 being a child of a child of a child, etc).
I'd am trying to query based on a single part and get all of its children and their children, so on.
Its seems so simple but I have been struggling with this one. Anyone have a solution they can offer?
As always, I appreciate the help.
Per Randy's request, here is some more info:
PARPRT = Parent
COMPRT = Component
In the screen shot, all the component parts are part of the Bill of Materials for '101002'.
Later down the table, you will see each of those components in COMPRT listed in the PARPRT column with the components that make each of them up.
I want to query the BOM for '101002' and not only get the 4 parts in COMPRT where PARPRT = '101002', but also their COMPRTs and so on.
Thank you for those who suggested CTE's. That was exactly what I was looking for.
Here is the query I ended up with after some trial and error.
USE PartDatabase
GO
DECLARE #TheSinglePart CHAR(30) = '100001';
WITH BOM (PARPRT_02, COMPRT_02, QTYPER_02)
AS
(
-- Anchor member definition
SELECT e.PARPRT_02, e.COMPRT_02, e.QTYPER_02
FROM dbo.Product_Structure AS e
WHERE e.PARPRT_02 = #TheSinglePart
UNION ALL
-- Recursive member definition
SELECT e.PARPRT_02, e.COMPRT_02, e.QTYPER_02
FROM dbo.Product_Structure AS e
INNER JOIN BOM AS d
ON e.PARPRT_02 = d.COMPRT_02
)
SELECT *
FROM BOM;

SQL counter difficulties

select maintitle,
firstprodyear,
COUNT(DISTINCT episode.episodeid) as TOTALEPISODES
from series
LEFT OUTER JOIN episode ON series.seriesid = episode.seriesid
LEFT OUTER JOIN filmitem ON filmitem.filmid = episode.episodeid
where firstprodyear =(select MIN(firstprodyear) from series)
group by maintitle, firstprodyear;
2/3s of the query works. I do get the title of the serie and earliest year. But it seems like the episode counter isn't working properly. For some episode I do get 15, 34 and somewhere 0.
I would preciate for some guidance to make the episodecounter work as it should. Where have I missed?
Try:
select maintitle,
min(firstprodyear) firstprodyear,
COUNT(DISTINCT episode.episodeid) as TOTALEPISODES
from series
LEFT OUTER JOIN episode ON series.seriesid = episode.seriesid
/*LEFT OUTER JOIN filmitem ON filmitem.filmid = episode.episodeid */
group by maintitle;
Note: the link to filmitem appears to be unnecessary with the data selected.
Your query is returning the series that have firstprodyear equal to the earliest firstprodyear in the your database. You can think of the sub-select statement as returning a fixed number that is then used in the query.
For example, if the earliest series in your database is Days of Our Lives (firstprodyear = 1965), then you will get back the number of episodes in those series that also started in 1965.
You may also want to be more explicit about which table actually contains the firstprodyear field, though I'm assuming it's series.

MySQL to return only last date / time record

We have a database that stores vehicle's gps position, date, time, vehicle identification, lat, long, speed, etc., every minute.
The following select pulls each vehicle position and info, but the problem is that returns the first record, and I need the last record (current position), based on date (datagps.Fecha) and time (datagps.Hora). This is the select:
SELECT configgps.Fichagps,
datacar.Ficha,
groups.Nombre,
datagps.Hora,
datagps.Fecha,
datagps.Velocidad,
datagps.Status,
datagps.Calleune,
datagps.Calletowo,
datagps.Temp,
datagps.Longitud,
datagps.Latitud,
datagps.Evento,
datagps.Direccion,
datagps.Provincia
FROM asigvehiculos
INNER JOIN datacar ON (asigvehiculos.Iddatacar = datacar.Id)
INNER JOIN configgps ON (datacar.Configgps = configgps.Id)
INNER JOIN clientdata ON (asigvehiculos.Idgroup = clientdata.group)
INNER JOIN groups ON (clientdata.group = groups.Id)
INNER JOIN datagps ON (configgps.Fichagps = datagps.Fichagps)
Group by Fichagps;
I need same result I'm getting, but instead of the older record I need the most recent
(LAST datagps.Fecha / datagps.Hora).
How can I accomplish this?
Add ORDER BY datagps.Fecha DESC, datagps.Hora DESC LIMIT 1 to your query.
I'm not sure why you are having any problems with this as Lex's answers seem good.
I would start putting ORDER BY's in your query so it puts them in an order, when it's showing the record you want as the first one in the list, then add the LIMIT.
If you want the most recent, then the following should be good enough:
ORDER BY datagps.Fecha DESC, datagps.Hora DESC
If you simply want the record that was added to the database most recently (irregardless of the date/time fields), then you could (assuming you have an auto-incremental primary key in the datagps table (I assume it's called dataID for this example)):
ORDER BY datagps.dataID DESC
If these aren't showing the data you want - then there is something missing from your example (maybe data-types aren't DATETIME fields? - if not - then maybe a CONVERT to change them from their current type before ORDERing BY would be a good idea)
EDIT:
I've seen the screenshot and I'm confused as to what the issue is still. That appears to be showing everything in order. Are you implying that there are many more than 5 records? How many are you expecting?
Do you mean: for each record returned, you want the one row from the table datagps with the latest date and time attached to the result? If so, how about this:
# To show how the query will be executed
# comment to return actual results
EXPLAIN
SELECT
configgps.Fichagps, datacar.Ficha, groups.Nombre, datagps.Hora, datagps.Fecha,
datagps.Velocidad, datagps.Status, datagps.Calleune, datagps.Calletowo,
datagps.Temp, datagps.Longitud, datagps.Latitud, datagps.Evento,
datagps.Direccion, datagps.Provincia
FROM asigvehiculos
INNER JOIN datacar ON (asigvehiculos.Iddatacar = datacar.Id)
INNER JOIN configgps ON (datacar.Configgps = configgps.Id)
INNER JOIN clientdata ON (asigvehiculos.Idgroup = clientdata.group)
INNER JOIN groups ON (clientdata.group = groups.Id)
INNER JOIN datagps ON (configgps.Fichagps = datagps.Fichagps)
########### Add this section
LEFT JOIN datagps b ON (
configgps.Fichagps = b.Fichagps
# wrong condition
#AND datagps.Hora < b.Hora
#AND datagps.Fecha < b.Fecha)
# might prevent indexes to be used
AND (datagps.Fecha < b.Fecha OR (datagps.Fecha = b.Fecha AND datagps.Hora < b.Hora))
WHERE b.Fichagps IS NULL
###########
Group by configgps.Fichagps;
Similar question here only that that one uses outer joins.
Edit (again):
The conditions are wrong so corrected it. Can you show us the output of the above EXPLAIN query so we can pinpoint where the bottle neck is?
As hurikhan77 said, it will be better if you could convert both of the the columns into a single datetime field - though I'm guessing this would not be possible for your case (since your database is already being used?)
Though if you can convert it, the condition (on the join) would become:
AND datagps.FechaHora < b.FechaHora
After that, add an index for datagps.FechaHora and the query would be fast(er).
What you probably want is getting the maximum of (Fecha,Hora) per grouped dataset? This is a little complicated to accomplish with your column types. You should combine Fecha and Hora into one column of type DATETIME. Then it's easy to just SELECT MAX(FechaHora) ... GROUP BY Fichagps.
It could have helped if you posted your table structure to understand the problem.