views:

443

answers:

3

SpousesTable SpouseID

SpousePreviousAddressesTable PreviousAddressID, SpouseID, FromDate, AddressTypeID

What I have now is updating the most recent for the whole table and assigning the most recent regardless of SpouseID the AddressTypeID = 1

I want to assign the most recent SpousePreviousAddress.AddressTypeID = 1 for each unique SpouseID in the SpousePreviousAddresses table.

UPDATE spa 
SET spa.AddressTypeID = 1
FROM SpousePreviousAddresses AS spa INNER JOIN Spouses ON spa.SpouseID = Spouses.SpouseID,
(SELECT TOP 1 SpousePreviousAddresses.* FROM SpousePreviousAddresses 
 INNER JOIN Spouses AS s ON SpousePreviousAddresses.SpouseID = s.SpouseID 
 WHERE SpousePreviousAddresses.CountryID = 181 ORDER BY SpousePreviousAddresses.FromDate DESC) as us
WHERE spa.PreviousAddressID = us.PreviousAddressID

I think I need a group by but my sql isn't all that hot. Thanks.

Update that is Working

I was wrong about having found a solution to this earlier. Below is the solution I am going with

WITH result AS
(
    SELECT ROW_NUMBER() OVER (PARTITION BY SpouseID ORDER BY FromDate DESC) AS rowNumber, *
    FROM SpousePreviousAddresses
    WHERE CountryID = 181
)
UPDATE result
SET AddressTypeID = 1
FROM result WHERE rowNumber = 1
A: 
UPDATE spa SET spa.AddressTypeID = 1 
     WHERE spa.SpouseID IN (
         SELECT DISTINCT s1.SpouseID FROM Spa S1, SpousePreviousAddresses S2
              WHERE s1.SpouseID = s2.SpouseID 
                  AND s2.CountryID = 181 
                  AND s1.PreviousAddressId = s2.PreviousAddressId
              ORDER BY S2.FromDate DESC)

Just a guess.

FlySwat
thanks but I received this error on that attempt.The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified.Will keep looking and get back on it.
TEEKAY
+1  A: 

Here's one way to do it:

UPDATE spa1
SET spa1.AddressTypeID = 1
FROM SpousePreviousAddresses AS spa1 
  LEFT OUTER JOIN SpousePreviousAddresses AS spa2
    ON (spa1.SpouseID = spa2.SpouseID AND spa1.FromDate < spa2.FromDate)
WHERE spa1.CountryID = 181 AND spa2.SpouseID IS NULL;

In other words, update the row spa1 for which no other row spa2 exists with the same spouse and a greater (more recent) date.

There's exactly one row for each value of SpouseID that has the greatest date compared to all other rows (if any) with the same SpouseID.

There's no need to use a GROUP BY, because there's kind of an implicit grouping done by the join.

update: I think you misunderstand the purpose of the OUTER JOIN. If there is no row spa2 that matches all the join conditions, then all columns of spa2.* are returned as NULL. That's how outer joins work. So you can search for the cases where spa1 has no matching row spa2 by testing that spa2.SpouseID IS NULL.

Bill Karwin
Thank you this worked. I only had to change the where clause to WHERE spa1.CountryID = 181 AND spa2.AddressTypeID = 3;(the spouse id won't ever be null and I failed to mention that they were all updated to 3 initially)Thanks for the help.
TEEKAY
RE: update looking at this again your query updates the same row as my original query. My was also wrong.Please take a look at my updated question for my current solution.
TEEKAY
+4  A: 

Presuming you are using SQLServer 2005 (based on the error message you got from the previous attempt) probably the most straightforward way to do this would be to use the ROW_NUMBER() Function couple with a Common Table Expression, I think this might do what you are looking for:

WITH result AS
(
SELECT 
    ROW_NUMBER() OVER (PARTITION BY SpouseID ORDER BY FromDate DESC) as rowNumber,
    * 
FROM 
    SpousePreviousAddresses
)
    UPDATE SpousePreviousAddresses
    SET
     AddressTypeID = 2
    FROM 
     SpousePreviousAddresses spa
      INNER JOIN result r ON spa.SpouseId = r.SpouseId
    WHERE r.rowNumber = 1
      AND spa.PreviousAddressID = r.PreviousAddressID
            AND spa.CountryID = 181

In SQLServer2005 the ROW_NUMBER() function is one of the most powerful around. It is very usefull in lots of situations. The time spent learning about it will be re-paid many times over.

The CTE is used to simplyfy the code abit, as it removes the need for a temporary table of some kind to store the itermediate result.

The resulting query should be fast and efficient. I know the select in the CTE uses *, which is a bit of overkill as we dont need all the columns, but it may help to show what is happening if anyone want to see what is happening inside the query.

jheppinstall
Thanks for your help I appreciate it. I will definitely take a look at the row_number details. This however did not get the job done. It updated only the entry with the newest FromDate. I will keep looking at it but thanks.
TEEKAY
after re-looking at my solution this is more on track with my current solution, thank you for help.
TEEKAY