tags:

views:

61

answers:

2

I have a record set for inspections of many peices of equipment. The four cols of interest are equip_id,month,year,myData.

My requirment is to have EXACTLY ONE Record per month for each peice of equipment. I have a quiery that makes the data unique over equip_id,month,year. So there is no more than one record for each month/year for a peice of equipment. But now I need to simulate data for the missing month. I want to simply go back in time to get the last peice of my data.

So that may seem confusing, so i'll show by example.

Given this:
equip_id  month  year  myData
1         1      2010  500
1         2      2010  600
1         5      2010  800
2         2      2010  300
2         4      2010  400
2         6      2010  500

I want this:
equip_id  month  year  myData
1         1      2010  500
1         2      2010  600
1         3      2010  600
1         4      2010  600
1         5      2010  800
2         2      2010  300
2         3      2010  300
2         4      2010  400
2         5      2010  400
2         6      2010  500

Notice that im filling in missing data with the data from the month ( or two months etc.) before. Also note that if the first record for equip 2 is in 2/2010 than I don't need a record for 1/2010 even though I have one for equip 1.

I just need exactly one record for each month/year for each peice of equipment. So if the record does not exsist I just want to go back in time and grab the data for that record.

Thanks!!!

A: 

I've adjusted to account for year and month... The primary principles remain the same as the original queries presented where just the month. However, for applying a month and year, you need to test for the SET of YEAR + MONTH, ie: what happens if Nov/2009, then jump to Feb/2010, You can't rely on just a month being less than another, but the "set". So, I've apply the year * 12 + month to prevent a false value such as Nov=11 + year=2009 = 2009+11 = 2020, then Feb=2 of year=2010 = 2010+2 = 2012... But 2009*12 = 24108 + Nov = 11 = 24119 compared to 2010*12 = 24120 + Feb =2 = 24122 -- retains proper sequence per year/month combination. The rest of the principles apply. However, one additional, I created a table to represent the span of years to consider. For my testing, I added a sample Equip_ID = 1 entry with a Nov-2009, and Equip_ID = 2 with a Feb-2011 entry and the proper roll-over works too. (Table C_Years, column = year and values of 2009, 2010, 2011)

SELECT 
      PYML.Equip_ID,
      PYML.Year,
      PYML.Mth,
      P1.MyData
   FROM 
      ( SELECT 
             PAll.Equip_ID,
             PAll.Year,
             PAll.Mth,
             ( SELECT MAX( P1.Year*12+P1.Mth )
                   FROM C_Preset P1
                   WHERE PAll.Equip_ID = P1.Equip_ID
                     AND P1.Year*12+P1.Mth <= PAll.CurYrMth) as MaxYrMth
           FROM 
              ( SELECT 
                      PYM1.Equip_ID,
                      Y1.Year,
                      M1.Mth,
                      Y1.Year*12+M1.Mth as CurYrMth
                   FROM 
                      ( SELECT p.equip_id,
                               MIN( p.year*12+p.mth ) as MinYrMth,
                               MAX( p.year*12+p.mth ) as MaxYrMth
                            FROM 
                               C_Preset p
                            group by 
                               1
                      ) PYM1,
                      C_Years Y1,
                      C_Months M1
                   WHERE 
                          Y1.Year*12+M1.Mth >= PYM1.MinYrMth
                      AND Y1.Year*12+M1.Mth <= PYM1.MaxYrMth
              ) PAll
      ) PYML,
      C_Preset P1
   WHERE 
          PYML.Equip_ID = P1.Equip_ID
      AND PYML.MaxYrMth = P1.Year*12+P1.Mth

If this is going to be a repetative thing/report, I would just create a temporary table with 12 months -- then use that as the primary table, and do a left OUTER join to the rest of your data. This way, you know you'll always get every month, but only when a valid join to the "other side" is identified, you'll get that data too. Ooops... missed your point about the filling in missing elements from the last element... Thinking...

The following works... and I'll describe the elements to what is going on. First, I created a temp table "C_Months" with a column Mth (month) with numbers 1-12. I used "Mth" as an abbreviation of Month to not cause possible conflict with POSSIBLE reserved word MONTH. Additionally, in my query, the table reference "C_Preset" is the prepared set of data you mentioned you already have of distinct elements.

SELECT 
      LVM.Equip_ID,
      LVM.Mth,
      P1.Year,
      P1.MyData
   FROM 
      ( SELECT 
              JEM.Equip_ID,
              JEM.Mth,
              ( SELECT MAX( P.Mth ) 
                   FROM C_Preset P
                   WHERE P.Equip_ID = JEM.Equip_ID
                     AND P.Mth <= JEM.Mth ) as MaxMth
           FROM 
              ( SELECT distinct 
                      p.equip_id,
                      c.mth
                   FROM 
                      C_months c,
                      C_Preset p
                   group by 
                      1, 2
                   HAVING 
                          c.mth >= MIN( p.Mth )
                      and c.mth <= MAX( p.Mth )
                   ORDER BY 
                      1, 2 ) JEM
      ) LVM,
      C_Preset P1
   WHERE 
          LVM.Equip_ID = P1.Equip_ID
      AND LVM.MaxMth = P1.Mth
   ORDER BY 
      1, 2

The inner most query is a query of the available months (C_Months) associated with a given equipment ID. In your example, equipment ID 1 had a values of 1,2,5. So this would return 1, 2, 3, 4, 5. And for Equipment ID 2, it started with 2, but ended with 6, so it would return 2, 3, 4, 5, 6. Hence the aliased reference JEM (Just Equipment Months)

Then, the field selection for MaxMth (Maximum month)... This is the TRICKY ONE

      ( SELECT MAX( P.Mth ) 
           FROM C_Preset P
           WHERE P.Equip_ID = JEM.Equip_ID
             AND P.Mth <= JEM.Mth ) as MaxMth

From this, stating I want the maximum month AVAILABLE (from JEM) associated with the given equipment that is AT OR LESS than the month In question (detecting the highest "valid" equipment item/month within the qualified list. The result of this would result in...

Equip_ID Mth    MaxMth
  1      1      1
  1      2      2
  1      3      2
  1      4      2
  1      5      5

  2      2      2
  2      3      2
  2      4      4
  2      5      4
  2      6      6

So, for your example of ID = 1, you had months 1, 2, 5 (3 and 4 were missing), so the last valid month that 3 and 4 would refer to is sequence #2's month. Likewise for ID = 2, you had months 2, 4 and 6... Here, 3 would refer back to 2, 5 would refer back to 4.

The rest is the easy part. Now, we join your LVM (Last Valid Month) result as shown above to your original C_Preset (less records). But since we now have the last valid month to directly associate to an existing record in the C_Preset, we join by equipment id and the MaxMth colum, and NOT THE ACTUAL month.

Hope this helps... Again, you'll probably have to change my "mth" column references to "month" to match your format.

DRapp
This is not a normal way, in the db meaning, to design such a table.
Remou
As a temp table for something like this instead of trying to force a union 12 times (or more if given full lenght of month/year combinations) easier to join with such generic.
DRapp
You should not need a union 12 times or more.
Remou
Maybe I could use that (data table left join unique table) a and then say something like select top(1) data from a where data is null and Month < MonthForDataWithNullValue and Year < Year For DataWithNullValue order by year desc,month desc... or something like that...
kralco626
Were you successful at any of the samples provided for your question... if so, please check the answer that worked... if not, let us know where we are short to get resolution. When you build up your "Acceptance Rate", more people will help offering solutions.
DRapp
+3  A: 

By no means perfect:

SELECT equip_id, month, mydata
FROM (

   SELECT equip_id, month, mydata FROM equip
   UNION ALL
   SELECT EquipNum.equip_id, EquipNum.Num, 
     (SELECT Top 1 mydata 
      FROM equip 
      WHERE equip.month<n.num And equip.equip_id=equipnum.equip_id 
      ORDER BY equip.month desc) AS Data
   FROM 
     (SELECT e.equip_id, n.Num 
      FROM 
        (SELECT DISTINCT equip_id FROM equip)  AS e, 
     Numbers AS n)  AS EquipNum 
   LEFT JOIN equip 
   ON (EquipNum.Num = equip.month) 
   AND (EquipNum.equip_id = equip.equip_id)
   WHERE EquipNum.Num<DMax("month","equip")
   AND 
     (SELECT top 1 mydata 
      FROM equip 
      WHERE equip.month<n.num And equip.equip_id=equipnum.equip_id 
      ORDER BY equip.month desc) Is Not Null 
    AND equip.equip_id Is Null AND equip.Month Is Null) AS x
ORDER BY equip_id, month

For this to work you need a Numbers table, in this case it needs only hold integers from 1 to 12. The numbers table I used is called Numbers and the field is called Num.

EDIT re years comment

SELECT equip_id, year, month, mydata
FROM (

   SELECT equip_id, year, month, mydata FROM equip
   UNION ALL
   SELECT en.equip_id, en.year, en.Num, (SELECT Top 1 mydata 
      FROM equip e
      WHERE e.month<n.num And e.year=en.year And e.equip_id=en.equip_id 
      ORDER BY e.month desc) AS Data
   FROM (SELECT e.equip_id, n.Num, y.year
      FROM 
    (SELECT DISTINCT equip_id FROM equip)  AS e, 
    Numbers AS n, 
    (SELECT DISTINCT year FROM equip)  AS y)  AS en 
   LEFT JOIN equip AS e ON en.equip_id = e.equip_id
   AND en.year = e.year
   AND en.Num = e.month
   WHERE en.Num<DMax("month","equip") AND 
     (SELECT Top 1 mydata 
      FROM equip e
      WHERE e.month<n.num And e.year=en.year And e.equip_id=en.equip_id 
      ORDER BY e.month desc) Is Not Null
   AND e.equip_id Is Null 
   AND e.Month Is Null) AS x

ORDER BY equip_id, year, month
Remou
Wow! Thanks for your answer! However... Year is not included anywhere in that query and I have data goign back to 2007... am i missing something?
kralco626
Nope. I just ignored year :)
Remou
I notice I also made a mistake in tidying, which I have now corrected. I will look at year.
Remou
Okay, with year added
Remou
Couple of things I don't understand. you say year = year, but what if I need to fill in Jan 2010 from Dec 2009? and whats en.Num<DMax("month","equip") doing? seems like year should be involved there... BTW. Very impressive sql lol... never could have done that myself...
kralco626
Also I was thinking.. what if I created a query that created a table of the years and months requests(dynamically) and then i left joined that with my unique table of records. Then to fill in the null values I did something like this: SELECT equip_id as e_id,year,month,(select top 1 runhours from qry_1_c_One_Record_per_Month a where a.equip_id = e_id order by year,month)FROM qry_1_c_One_Record_per_Monthwhere runhours is null or runhours = 0;UNIONSELECT equip_id, year, month, runhoursFROM qry_1_c_One_Record_per_MonthWHERE .runhours Is Not Null And runhours <> 0;
kralco626
of course the inner select quiery doesnt really work because i can't conpare the equip_id to the e_id in the main select to make sure that I am getting the data for the correct equip_id... just a thought...
kralco626
i suppose it would work if I ran the query for only one equip_id... but i don't really want to limit my functionality like that...
kralco626
If, instead of year, month you convert to a real date, even in a subquery, it will make the whole thing simpler.
Remou
your gunna kill me... it started out as a real date. the actualy query has the id, the data, year, month,date. but I considered the date meaningless because i needed one record per month... so why does the day and time matter? I had to break the month and year out to do the query to get the unique value for each month...
kralco626
Because `datex < date` is a lot less complicated and more likely to return the data you want than `monthx < month and yearx=year`
Remou
well thats why i did the order by year,month. same as order by date i guess... i don't care which I use, I have both. Do you think there is a way to make the query i suggested work?
kralco626
I think you should post it in a new thread referencing this one. And include real (ish) data, that is, more than one year and date. It can be irritating to work on a solution only to find that data is incomplete or inaccurate.
Remou
O.K. I would have included the additional information, like the real date, but it didn't even occur to me that it would be helpfully. The only reason i even kept it in my query was to verify it was working corretly... Guess I shouldnt assume...
kralco626
Yep :) Especially seeing some of the people here can make data jump through hoops :)
Remou
O.K. I reposted the question. I just wanna thank you so so so much for all your help. And sorry I didn't give you a complete picture of the situation. The new question has a COMPLETE pciture. it's @ http://stackoverflow.com/questions/2929501/access-qry-questionsThanks again!!!
kralco626