tags:

views:

56

answers:

4

is it possible to create a per month identity ? basically what I need is

create table
(
id identity primary key
regDate DateTime not null,
regDateNumber int not null
)

and when I insert data I need set the regDateNumber as an identity in the scope of the regDate's month

like this:

id   regDate    regDateNumber
1    1 jan 2008 1
2    3 jan 2008 2
3    7 jan 2008 3
4    1 feb 2008 1
5    1 feb 2008 2
6    1 aug 2008 1
7    1 aug 2008 2
8    1 sep 2008 1
9    1 sep 2008 2
+2  A: 

You cannot do it with identity, so you may compute on fly. Something like:

select id, regDate, 
row_number() over(partition by year(regDate), month(regDate) as regDateNumber order by id) 
from t
actually I don't need to partition it on the fly when selecting, I need to compute when I do the insert
Omu
You can try do it with trigger. I do not advice this, because it is very error prone.Possibly if you describe how are going to use this field, we'll find better solution
+2  A: 

You could simply add a computed column to your table:

ALTER TABLE dbo.YourTable
  ADD regMonth AS MONTH(regDate) PERSISTED

and be done with it. This will be computed, persisted (e.g. not recomputed on every access, but only when regDate changes), contains the MONTH information for the date, and can even be indexed for speed.

Update: ok, so now I get it - your "identity per month" is a bit unclear and misleading. What you want is to sequentially number the entries for each month, right?

One way to do it is not doing it / not storing it, but using burnall's approach with the ROW_NUMBER() function.

Otherwise, you'd have to do an AFTER INSERT trigger which calculates the new value upon INSERT - but that gets a) messy and b) will cost you quite a bit of performance when you're doing lots of inserts.

My recommendation would be to use burnall's function and let that run e.g. once every hour or so to update those fields. Do not try to figure this out on the fly, upon INSERT - your performance will be awful.

marc_s
how is this going to help me generate the regDateNumber when I do the insert ?
Omu
but I need a per month identity, not just a month
Omu
I need to set it on insert because if somebody will delete some records I should get a list or records like 1,2,5,6 instead of 1,2,3,4 (in your case)
Omu
@Omu: if you **MUST** - then you need to create an AFTER INSERT trigger. But again: I would not recommend that. Too much overhead, too big an impact on performance.
marc_s
There are lots possible appoaches depending of problem details which were not supplied. Hardly to suggest how far it should be optimized.Just another optimization idea: you can persist year*12+month and create index for it and use row_number() over(partition by newCol order by id)
@burnall the problem details is that I have to know the insert order number in a specific month for each record
Omu
@Omu - How are you going to cope with the case that someone deletes the last record? In that case when you insert the next record it will reuse that same sequence number and you won't get the gap in the sequence that you want unless you store the max sequence number somewhere else. Maybe have a "deleted" flag rather than actually deleting the record then it will be easy to calculate the sequence number as burnall suggests.
Martin Smith
+1  A: 

I believe you can achieve this by using a stored procedure:

CREATE PROCEDURE month_indentity
   @myInput  DateTime
AS
SELECT COUNT(*) + 1 FROM mytable
WHERE MONTH(regDate) = MONTH(@myInput)
    AND YEAR(regDate) = YEAR(@myInput)
GO
Anax
this is how I could calculate the value inside a SP for insert, I believe some table locking is required for this ? could it happen if there are going to be 2 calls to this sp at the same time, could I get 2 records with same value for regDateNumber
Omu
I don't think this will work properly in a busy server environment - you'll end up with duplicate values for your "month_identity" since you're not really considering concurrency at all here.....
marc_s
+1  A: 
create table #table (iid int identity (1, 1), dy int, mth varchar(3))

insert into #table (dy, mth)
select 1, 'jan' union all
select 2, 'jan' union all
select 20, 'jan' union all
select 10, 'jan' union all
select 1, 'feb' union all
select 3, 'feb' union all
select 28, 'feb' 

select T.dy, T.mth, 
  (select count(X.iid) from #table X where X.mth = T.mth and X.iid < T.iid) + 1 as month_rank
from #table T
order by T.mth, month_rank

I wouldn't store the value as a calculated field since it can change over time and it violates normalization rules.

souLTower