Select a DISTINCT ID, then pull data from another table - mysql

I have 2 MySQL tables, one for parts, and one for years. I can't figure out how to make a table on stackoverflow.. keeps making headers so here's my weak attempt to explain what I need.
Table 1
id | part_id | years
====================
0 | 15 | 1945
1 | 15 | 1946
2 | 16 | 1944
3 | 16 | 1947
4 | 16 | 1948
5 | 17 | 1953
As you may have guessed, part_id is the id number of the part in the parts table. Now, I know I have this to pull out a distinct part id, based on the year. That part is easy.
SELECT DISTINCT part_id FROM `years` WHERE year BETWEEN 1945 AND 1949
This is just an example, but that works exactly like I want it to. Gives me
15 and 16. Just one time. Which is great.
Now, do I need to do a loop in php to get the information from parts? I'm not sure how to do a join here.
<?php
foreach($item_pulled_from_db as $newid) {
$query = "SELECT * FROM 'parts' WHERE id = $newid";
} // I know there's more stuff to do in here, just a basic overview for you to look at
?>
Should I do the above code? Is there a way to select a DISTINCT part_id and then pull the data from another table for that ID in MySQL? Or do I have to do a loop like this?
Edit: I hope this isn't too confusing of a question. I'm not very good with words, which is why I like to program. :)

Use a join:
SELECT parts.*
FROM parts
JOIN (SELECT DISTINCT part_id
FROM years
WHERE year BETWEEN 1945 AND 1949) years
ON parts.id = years.part_id

You could pull this off using a JOIN in a single query. Try:
SELECT `parts`.* FROM `parts`
INNER JOIN `years` ON `years`.`part_id` = `parts`.`id`
WHERE `years`.`year` BETWEEN 1945 AND 1949
Execute that single query from PHP and then fetch the result set. It should be the same as what you would get using the multiple queries.

This query give you the result you want:
SELECT DISTINCT
p.*
FROM
years y
INNER JOIN
parts p ON p.id = y.part_id
WHERE
y.year BETWEEN 1945 AND 1949

Related

Query to include non-existent rows as 0 value

Although there are a number of similar questions on SO, I could not find any answer that would give me the desired output.
Situation: table "purchaseorders" containing POs with various details. Example record:
| id | orderId | number | vendorNumber | grossValue | bookingYear |
|----|---------|--------|--------------|------------|-------------|
| 14 | 691 | 1 | 12345 | 152.43 | 2016 |
I want aggregate sums(grossValue) for specific vendors for the past 5 years, including 0 values.
Required output:
Current actual output:
My attempt at SQL:
SELECT po.bookingYear, IFNULL (sum(po.grossValue),0) FROM purchaseorder as po
LEFT JOIN (select 2016 as yr UNION ALL select 2017 as yr UNION ALL select 2018 as yr UNION ALL select 2019 as yr UNION ALL select 2020 as yr) as years ON po.bookingYear=years.yr
WHERE po.vendorNumber=12345 group by po.bookingYear;
I started out with a simple "WHERE bookingYear IN (...)", but since there are no records for the past three years, none are returned. Searching here and on Google suggested I left join a subquery like above (though probably not exactly like above).
I think the biggest issue here is, that I don't have NULL values for certain fields in certain years but simply no records at all for those years for some vendors.
Can anyone point me in the right direction? My SQL has become a bit rusty it seems. Speaking of: yes: mySQL.
Right idea. Wrong order. The years go first:
SELECT years.yr, COALESCE(SUM(po.grossValue), 0)
FROM (select 2016 as yr UNION ALL select 2017 as yr UNION ALL select 2018 as yr UNION ALL select 2019 as yr UNION ALL select 2020 as yr
) years LEFT JOIN
purchaseorder po
ON po.bookingYear = years.yr AND
po.vendorNumber = 12345
GROUP BY years.yr;
Note the other changes to the query:
The filtering on vendorNumber is now in the ON clause rather then WHERE clause. In the WHERE clause it would turn the outer join to an inner join.
The GROUP BY and SELECT use years.yr. The value po.bookingYear might be NULL.
I prefer COALESCE(), the SQL standard function, to IFNULL() a bespoke MySQL function.
If you are running MySQL 8.0, you can use a recursive query to generate the dates. This incurs less typing than union all if you have a lot of years:
with years as (
select 2016 bookingYear
union all select bookingYear + 1 from years where bookingYear < 2020
)
select y.bookingYear, coalesce(sum(po.grossValue), 0) grossValue
from years y
left join purchaseorder po on po.bookingYear = y.bookingYear and po.vendorNumber = 12345
group by y.bookingYear

MySQL Query for Counts from Multiple Tables

I've started a SQL Fiddle here:
http://sqlfiddle.com/#!2/26c26/7
You can view that sample table and see that I've got multiple car models across three tables. I'm trying to get a 6 column layout. The first two columns would be JeepMart1 models, and the count of those models, then the next two columns would the JeepMart2 models, and counts and the last two columns would be JeepMart3 models and counts.
The results of this query from the fiddle table would look like:
Jeep1Models | Jeep1Counts | Jeep2Models | Jeep2Counts | Jeep3Models | Jeep3Counts |
Wrangler 3 Wrangler 1
Grand Cherokee 2 Grand Cherokee 3
Patriot 1 Patriot 5
The idea is to get a count of each model from each JeepMart.
If it's at all possible, it would be the cherry on top to get the totals of each model like this: (shortened the col names for readability)
Jeep1Mod | Jeep1Cou | Jeep2Mod | Jeep2Cou | Jeep3Mod | Jeep3Cou | Totals
Wrangler 3 Wrangler 1 4
Grand Cher 2 Grand Cher 3 5
Patriot 1 Patriot 5 6
I am completely open to querying this data in another way, even if it means changing the layout of the results, as long as all the results are there, and are just as easy to use.
you want to COUNT(DISTINCT vin) like below
SELECT J1.model as Jeep1Model,
COUNT(DISTINCT J1.vin) as Jeep1Counts,
J2.model as Jeep2Model,
COUNT(DISTINCT J2.vin) as Jeep2Counts,
J3.model as Jeep3Model,
COUNT(DISTINCT J3.vin) as Jeep3Counts,
COUNT(DISTINCT J1.vin)+
COUNT(DISTINCT J2.vin)+
COUNT(DISTINCT J3.vin) as Total
FROM
(SELECT DISTINCT model FROM JeepMart1
UNION SELECT DISTINCT model FROM JeepMart2
UNION SELECT DISTINCT model FROM JeepMart3)A
LEFT JOIN JeepMart1 J1 ON J1.model = A.model
LEFT JOIN JeepMart2 J2 ON J2.model = A.model
LEFT JOIN JeepMart3 J3 ON J3.model = A.model
GROUP BY A.model
sqlFiddle

MySQL Group by week num w/ multiple date column

I have a table with columns similar to below , but with about 30 date columns and 500+ records
id | forcast_date | actual_date
1 10/01/2013 12/01/2013
2 03/01/2013 06/01/2013
3 05/01/2013 05/01/2013
4 10/01/2013 09/01/2013
and what I need to do is get a query with output similar to
week_no | count_forcast | count_actual
1 4 6
2 5 7
3 2 1
etc
My query is
SELECT weekofyear(forcast_date) as week_num,
COUNT(forcast_date) AS count_forcast ,
COUNT(actual_date) AS count_actual
FROM
table
GROUP BY
week_num
but what I am getting is the forcast_date counts repeated in each column, i.e.
week_no | count_forcast | count_actual
1 4 4
2 5 5
3 2 2
Can any one please tell me the best way to formulate the query to get what I need??
Thanks
try:
SELECT weekofyear(forcast_date) AS week_forcast,
COUNT(forcast_date) AS count_forcast, t2.count_actual
FROM
t t1 LEFT JOIN (
SELECT weekofyear(actual_date) AS week_actual,
COUNT(forcast_date) AS count_actual
FROM t
GROUP BY weekOfYear(actual_date)
) AS t2 ON weekofyear(forcast_date)=week_actual
GROUP BY
weekofyear(forcast_date), t2.count_actual
sqlFiddle
You have to write about 30 (your date columns) left join, and the requirement is that your first date column shouldn'd have empty week (with a count of 0) or the joins will miss.
Try:
SELECT WeekInYear, ForecastCount, ActualCount
FROM ( SELECT A.WeekInYear, A.ForecastCount, B.ActualCount FROM (
SELECT weekofyear(forecast_date) as WeekInYear,
COUNT(forecast_date) as ForecastCount, 0 as ActualCount
FROM TableWeeks
GROUP BY weekofyear(forecast_date)
) A
INNER JOIN
( SELECT * FROM
(
SELECT weekofyear(forecast_date) as WeekInYear,
0 as ForecastCount, COUNT(actual_date) as ActualCount
FROM TableWeeks
GROUP BY weekofyear(actual_date)
) ActualTable ) B
ON A.WeekInYear = B.WeekInYear)
AllTable
GROUP BY WeekInYear;
Here's my Fiddle Demo
Just in case someone else comes along with the same question:
Instead of trying to use some amazing query, I ended up creating an array of date_columns_names and a loop in the program that was calling this query, and for each date_column_name, performing teh asme query. It is a bit slower, but it does work

MySQL - Count Yearly Totals when some Years have nulls

I have 1 table with similar data:
CustomerID | ProjectID | DateListed | DateCompleted
123456 | 045 | 07-29-2010 | 04-03-2011
123456 | 123 | 10-12-2011 | 11-30-2011
123456 | 157 | 12-12-2011 | 02-10-2012
123456 | 258 | 06-07-2011 | NULL
Basically, a customer contacts us, we get a project on our list, and we mark it completed when we're done with it.
What I'm after is a simple (you'd think, at least) count of all projects, with expected output like below:
YEAR | TotalListed | TotalCompleted
2010 | 1 | 0
2011 | 3 | 2
2012 | 0 | 1
However, my query below - because of the join - isn't showing 2012's count, because there's been no listed project for 2012. However, I can't really reverse the query, as then 2010's count wouldn't show up (since nothing was completed in 2010).
I'm open to any suggestions, or tips like how to do this. I've pondered a temp table, is that the best way to go? I'm open to anything that gets me what I need!
(If the code looks familiar, ya'll helped me get the subquery made! MySQL Subquery with main query data variable)
SELECT YEAR(p1.DateListed) AS YearListed, COUNT(p1.ProjectID) As Listed, PreQuery.Completed
FROM(
SELECT YEAR(DateCompleted) AS YearCompleted, COUNT(ProjectID) AS Completed
FROM projects
WHERE CustomerID = 123456 AND DateListed >= DATE_SUB(Now(), INTERVAL 5 YEAR)
GROUP BY YEAR(DateCompleted)
) PreQuery
RIGHT OUTER JOIN projects p1 ON PreQuery.YearCompleted = YEAR(p1.DateListed)
WHERE CustomerID = 123456 AND DateListed >= DATE_SUB(Now(), INTERVAL 5 YEAR)
GROUP BY YearListed
ORDER BY p1.DateListed
After reviewing your table, query, and expected results - I believe I have found a more-revised query to suit your needs. It is a fairly-full rewrite of your existing query though, but I've tested it with your given data and received the same results you want/expect:
SELECT
years.`year`,
SUM(IF(YEAR(DateListed) = years.`year`, 1, 0)) AS TotalListed,
SUM(IF(YEAR(DateCompleted) = years.`year`, 1, 0)) AS TotalCompleted
FROM
projects
LEFT JOIN (
SELECT DISTINCT `year` FROM (
SELECT YEAR(DateListed) AS `year` FROM projects
UNION SELECT YEAR(DateCompleted) AS `year` FROM projects WHERE DateCompleted IS NOT NULL
) as year_inner
) AS years
ON YEAR(DateListed) = `year`
OR YEAR(DateCompleted) = `year`
WHERE
CustomerID = 123456 AND DateListed >= DATE_SUB(Now(), INTERVAL 5 YEAR)
GROUP BY
years.`year`
ORDER BY
years.`year`
To explain, we should start with the inner query (aliased as year_inner). It selects a full list of years in the DateListed and DateCompleted columns and then selects a DISTINCT list of those to create the years alias sub-query. This sub-query is used to get a full list of "years" that we want data for. Doing it this way, opposed to a sub-query with counts and groupings will allow you to only have to define the WHERE clause on the outermost query (though, if efficiency becomes an issue with thousands and thousands of records, you could always add a WHERE clause to the inner query too; or an index to the date columns).
After we've built our inner queries, we join the projects table on the results with a LEFT JOIN for the DateListed or DateCompleted's YEAR() value - which will allow us to bring back null columns too!
For the field selections, we use the year column from our inner query to assure that we get a full list of years to display. Then, we compare the current row's DateListed & DateCompleted YEAR() value to the current year; if they're equal, add 1 - else add 0. When we GROUP BY year, our SUM() will count all of the 1's for that year for each column and give you the output you want (hopefully, of course =P).

Counting distinct values for multiple months

Got a little problem here. I can't for the life of me, figure out how to do this.
pid | firstlast | lastvisit | zip
---------------------------------------
435 | 2001-01-17 | 2012-01-21 | 46530
567 | 2001-01-18 | 2012-01-21 | 46530
532 | 2001-01-19 | 2012-01-22 | 46535
536 | 2001-01-19 | 2012-01-23 | 46535
539 | 2001-01-20 | 2012-01-27 | 46521
Here is my SQL query:
SELECT DISTINCT zip, COUNT(zip) AS totalzip FROM production WHERE MONTH(lastvisit) = "1" GROUP BY zip ORDER BY totalzip DESC;
Output:
Jan:
zip | totalzip
---------------------
46530 | 2
46535 | 2
46521 | 1
Feb:
zip | totalzip
---------------------
46530 | 1
46521 | 4
49112 | 3
This is great for the 1st month, but I need this for the entire year. I could run this query 12 times, however 2 problems occur. I have over 300 zip codes for the entire year. On some months the zip code is not present, so the count is 0 (but the MySQL output doesn't output the "zero data". Also, when I order by totalzip, the order changes from month to month, and this does not allow me to paste them into a spread sheet. I can order by zip code, but again the "zero" data zipcodes are not present and so the list changes from month to month.
Any thoughts or suggestions would be much appreciated!
You can make this work with subqueries:
select
a.*, count(c.zip) as totalZip
from
(select
monthVisit, zip
from
(select distinct last_day(lastVisit) as monthVisit from production) as m,
(select distinct zip from production) as z
) as a
left join (select
last_day(lastVisit) as monthVisit, zip
from production) as c
on a.monthVisit=c.monthVisit and a.zip=c.zip
group by
a.monthVisit, a.zip
This should give you the count of zips for each month you have, including zeros.
Let me explain how this works:
First, I defined a subquery that makes all the possible combinations of zips and months (the a subquery), and then I left joined this with a second subquery that returns the values of ZIPs and months (the c subquery). Using left join allows to count the possible empty combinations in the a subquery.
Hope this help you.
Note: The last_day() function returns the last day of the month of a given date; e.g.: last_day('2012-07-17')='2012-07-31'
If you have a zipcode table (you should), you could join it with your data table (a left join), which would bring even the zero-count zipcodes.
The first part of your question is solved with additional grouping. Try something like this:
SELECT DISTINCT zip, YEAR(lastvisit), MONTH(lastvisit), COUNT(zip) AS totalzip
FROM production
GROUP BY zip, YEAR(lastvisit), MONTH(lastvisit)
ORDER BY totalzip DESC;
To add in the "zero" summaries when no data is present I typically do a left-join with a complete list. (This is also stated by #Alfabravo above). So the final query looks a bit like:
SELECT DISTINCT zip, YEAR(lastvisit), MONTH(lastvisit), COUNT(zip) AS totalzip
FROM production left join
(SELECT DISTINCT zip from production) as zipMap on zipmap.zip = production.zip
GROUP BY zip, YEAR(lastvisit), MONTH(lastvisit)
ORDER BY totalzip DESC;