Click here to Skip to main content
15,894,539 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have a table that looks something like this (the table also has a unique ID column):

name    value    month    year
John    0.50      11      2014
Mary    0.60      11      2014
Pete    0.60      11      2014
John    0.60      12      2014
Don     0.50      10      2013


If I pass 11 and 2014 to a stored proc, I want to retrieve one record for each of name, like so:

John    0.50      11      2014
Mary    0.60      11      2014
Pete    0.60      11      2014
Don     0.50      10      2013


If I pass 12 and 2014 to a stored proc, I want to retrieve one record for each of name, like so:

Mary    0.60      11      2014
Pete    0.60      11      2014
John    0.60      12      2014
Don     0.50      10      2013


If I pass 10 and 2014 to a stored proc, I want to retrieve one record for each of name, like so:

Mary    0.0       10      2014
Pete    0.0       10      2014
John    0.0       10      2014
Don     0.50      10      2013


How would I do that in sql server?
Posted
Updated 25-Nov-14 6:08am
v4
Comments
PIEBALDconsult 25-Nov-14 12:17pm    
What have you tried? :D

Actually, do you really mean 2013 for Don?
#realJSOP 25-Nov-14 12:21pm    
Honestly, I don't know how to approach it. I'm not even sure how to ask the freakin question (makes it real hard to google). My brain is completely failing to engage... :)


Yes on the year for Don. Not all names will have a changed value in a given year, and I wanted to return a valid row for each name regardless of stupid user tricks.
Further, new names might be added in the future which of course won't have past values.
PIEBALDconsult 25-Nov-14 12:39pm    
"a changed value"

Ah, so a value stays in effect until a new value is set, that's more complex.

Maybe this will help. LEFT JOIN the list of DISTINCT names to the data and use ISNULL:

(take two)

SQL
DECLARE @yr INTEGER = 2014
DECLARE @mn INTEGER = 10
DECLARE @ym INTEGER = @yr*100+@mn

;
WITH src AS
(
  SELECT 'John' [name] , 0.50 [value] , 11 [month] , 2014 [year]
UNION ALL
  SELECT 'Mary' 'name' , 0.60 'value' , 11 'month' , 2014 'year'
UNION ALL
  SELECT 'Pete' 'name' , 0.60 'value' , 11 'month' , 2014 'year'
UNION ALL
  SELECT 'John' 'name' , 0.60 'value' , 12 'month' , 2014 'year'
UNION ALL
  SELECT 'Don' 'name' , 0.50 'value' , 10 'month' , 2013 'year'
)
, lst AS
(
  SELECT [name]
  , [value]
  , [month]
  , [year]
  FROM (
    SELECT *
    , ROW_NUMBER() OVER ( PARTITION BY [name] ORDER BY [year] DESC , [month] DESC) RN
    FROM src
    WHERE [year]*100+[month]<=@ym
  ) T
  WHERE RN=1
)
, nam AS
(
  SELECT DISTINCT [name] FROM src
)
SELECT A.Name
, ISNULL(B.value,0) 'value'
, ISNULL(B.[month],@mn) [month]
, ISNULL(B.[year],@yr) [year]
FROM nam A
LEFT OUTER JOIN lst B
ON A.name=B.name
 
Share this answer
 
v2
Comments
#realJSOP 25-Nov-14 12:45pm    
works great for old dates that have no values for some names, but if I use 12 and 2014, if should give me the next earliest value (11 2014).
PIEBALDconsult 25-Nov-14 12:50pm    
Yep, updated.
PIEBALDconsult 25-Nov-14 13:03pm    
It should (I'm using 2012), are you getting an error?
http://msdn.microsoft.com/en-us/library/ms186734(v=sql.105).aspx
#realJSOP 25-Nov-14 13:04pm    
Yeah - it's the typical useless "problem by xyz" error
PIEBALDconsult 25-Nov-14 13:06pm    
And you have the semi-colon before the WITH ?
Try this one:
SQL
declare @mytable TABLE (
	[name] [nchar](10) NULL,
	[value] [numeric](10, 2) NULL,
	[month] [numeric](2, 0) NULL,
	[year] [numeric](4, 0) NULL
)

insert into @mytable
SELECT 'John' [name] , 0.50 [value] , 11 [month] , 2014 [year]
UNION ALL
  SELECT 'Mary' 'name' , 0.60 'value' , 11 'month' , 2014 'year'
UNION ALL
  SELECT 'Pete' 'name' , 0.60 'value' , 11 'month' , 2014 'year'
UNION ALL
  SELECT 'John' 'name' , 0.60 'value' , 12 'month' , 2014 'year'
UNION ALL
  SELECT 'Don' 'name' , 0.50 'value' , 10 'month' , 2013 'year'


declare @year numeric(4,0) = 2014
declare @month numeric(2,0) = 11;

WITH myCTE(name, my)
AS
(
	select distinct 
	t1.name,
	(select max(cast(cast(year as varchar) + '-' + cast(month as varchar) + '-01' as DATE))
		from @mytable 
		where cast(cast(@year as varchar) + '-' +cast(@month as varchar) + '-01' as DATE) >= cast(cast(year as varchar) + '-' + cast(month as varchar) + '-01' as DATE)
		and t1.name = name) as my
	from @mytable t1 
)
select myCTE.name, t2.value, DATEPART(MONTH, my) as month, DATEPART(YEAR, my) as year
from myCTE left join @mytable t2 on 
(myCTE.name = t2.name and myCTE.my = cast(cast(year as varchar) + '-' + cast(month as varchar) + '-01' as DATE))
 
Share this answer
 
v2
Comments
Zoltán Zörgő 26-Nov-14 12:40pm    
I would be interested why I got the downvote, as the solution - even not the best possible - is working as expected. :(
In addition to solution 1 and 2 i'd like to recommend a very good article about joins: Visual Representation of SQL Joins[^]. It might help you to understand how joins work.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900