GETDATE() won't be unique. It's accuracy is such that multiple near concurrent events can be supplied with the same time.
If you are forced to generate your own identity values, so as not to interfere with @@IDENTITY, then you can do the following...
INSERT INTO
myTable (
id,
field1,
field2
)
SELECT
(SELECT ISNULL(MAX(id), 0) FROM myTable WITH(TABLOCKX)) + 1,
@p1,
@p2
This is implicitly within it's own transaction and will guarantee unique values.
EDIT
My original comment was going to be that this will NOT work when inserting multiple records, and that instead you would need to iterate through the source records individually, inserting them one at a time.
The following example, however, may be suitable to you for processing SETs of data...
WITH
sorted_data AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY field1) AS set_id, -- DO NOT include a PARTITION here
*
FROM
inserted
)
INSERT INTO
myTable (
id,
field1,
field2
)
SELECT
(SELECT ISNULL(MAX(id), 0) FROM myTable WITH(TABLOCKX)) + set_id,
@p1,
@p2
FROM
sorted_data
This will both generate unique IDs for each row, and be safe against concurrent processes using the same code.
EDIT
I've added WITH(TABLOCKX)
to prevent other processes reading from the table while it's being updated. This prevents concurrent processes from establishing the same MAX(id) and then trying to insert duplicate id's in new records.
(The single query structure already prevented records from being altered after they had been read from, but did not prevent other processes reading from the table 'between' the MAX(id) being read and all the new records being inserted.)