Let's imagine a table below, where;
ID is the primary key and it is auto incremental column
ItemType is a foreign key
OrderID is the order number for each ItemType value
ID ItemType OrderID Col1
== ======== ======= ====
1 1 1 ABCD
2 1 2 XYZT
3 2 1 BDKL
4 1 3 XXXX
5 1 4 TYTY
6 2 2 ABCD
7 1 5 XYZZ
8 3 1 ABCD
9 3 2 ABCD
10 1 6 XYZT
11 2 3 ABCD
as you see there might be more than one ItemType that comes from another table, and each ItemType has a sequential OrderID that starts from 1 and increases by 1 for every record.
My Question is;
what is the best practice to have a column that keeps the OrderID information correctly?
Assuming that the ID values would always be increasing, such that a subsequent order's ID value would always be greater than an an earlier order's ID value, we could just use ROW_NUMBER here and not even use a column:
SELECT
ID,
ItemType,
ROW_NUMBER() OVER (PARTITION BY ItemType ORDER BY ID) OrderID,
Col1
FROM yourTable
ORDER BY
ID;
Demo
If my assumption of the ID column might not be correct always, then I suggest adding a new timestamp column which records when each order actually happened. Then, use something similar to the above approach, but order based on the order timestamp.
You do not need to do this - it will be difficult to implement and you can face some performance issues if batches of orders are created at the same time. As there is no built -in group by identity or identity over (partition by) you need to get the maximum value for each inserted type - and this should be in transaction and will be blocking others inserted.
So, just have a normal identity column to guarantee uniqueness of each order and use ROW_NUMBER to get the OrderID in incremented way by type in the presentation lair.
Related
This question already has answers here:
Get top 1 row of each group
(19 answers)
Closed last year.
I have 2 tables like above
table1 = items => id, name
table 2 = item_price => item_id, date,price
I need to
select items.name from items
INNER JOIN item_price ON items.id = item_price2.item_id
where item_price.date is last date
items
id name
1 item name 1
2 item name 2
3 item name 3
item_price
item_id date price
1 2022-01-13 5
2 2022-01-10 7
1 2022-01-10 4
1 2022-01-09 9
I need build query to get this result
item name price
item name 1 5
item name 2 7
thanks a lot
This can be solved by selecting the last row using the ROW_NUMBER windowed partition function.
WITH last_item_price (item_id, price, reverse_order)
AS
(SELECT item_id
, price
, ROW_NUMBER() OVER
(PARTITION BY item_id
ORDER BY date DESC) AS reverse_order
FROM item_price)
SELECT i.name
, p.price
FROM items i
JOIN last_item_price p
ON i.item_id = p.item_id
AND p.reverse_order = 1;
Unless the primary key of item_price is defined as the composite key for item_id and date, there is a risk you will have more than one possible price. For instance, if the price changes three times on a single day, then how do you know which is the correct value? I would recommend using a DATETIME or DATETIME2 field to help pick the correct price in that scenario. Alternatively, you can define the primary key as the composite of the two fields and only allow a single price each day.
Another way to avoid the duplication issue is to add an auto-incrementing identity column. This will not be useful for joins and I still strongly recommend using a composite key for the item_id and date fields, but it is a valid alternative. In that case, you can modify the partition function's ORDER BY clause to:
ORDER BY date DESC, item_price_id DESC
In general, I would not recommend naming a field date. Since SQL Server has a data type called DATE, this can cause issues with linters. In some cases, you may be required to use brackets so the field is [date] to disambiguate your intent. A better name is probably price_date or price_change_date.
I have a table that looks like this:
ID A B Count
-----------------
1 abc 0 1
2 abc 0 2
3 abc 1 1
4 xyz 1 1
5 xyz 1 2
6 xyz 1 3
7 abc 1 2
8 abc 0 3
The "Count" column is incremented by one in the next insertion depending on the value of fields "A" and "B". so for example, if the next record I want to insert is:
ID A B Count
-----------------
abc 0
The value of count will be 4.
I have been trying to find documentation about this, but I'm still quite lost in the MS SQL world! There must be a way to configure the "Count" column as a sequence dependent on the other two columns. My alternative would be to select all the records with A=abc and B=0, get the maximum "Count", and do +1 in the latest one, but I suspect there must be another way related to properly defining the Count column when creating the table.
The first question is: Why do you need this?
There is ROW_NUMBER() which will - provided the correct PARTITION BY in the OVER() clause - do this for you:
DECLARE #tbl TABLE(ID INT,A VARCHAR(10),B INT);
INSERT INTO #tbl VALUES
(1,'abc',0)
,(2,'abc',0)
,(3,'abc',1)
,(4,'xyz',1)
,(5,'xyz',1)
,(6,'xyz',1)
,(7,'abc',1)
,(8,'abc',0);
SELECT *
,ROW_NUMBER() OVER(PARTITION BY A,B ORDER BY ID)
FROM #tbl
ORDER BY ID;
The problem is: What happens if a row is changed or deleted?
If you write this values into a persistant column and one row is removed physically, you'll have a gap. Okay, one can live with this... But if a value in A is changed from abc to xyz (same applies to B of course) the whole approach breaks.
If you still want to write this into a column you can use the ROW_NUMBER() from above to fill these values initially and a TRIGGER to set the next value with your SELECT MAX()+1 approach for new rows.
If the set of combinations is limited you might create a SEQUENCE (needs v2012+) for each.
But - to be honest - the whole issue smells a bit.
I have a table with many rows that is similar to the one below where each EmployeeID is repeated twice. I simply want to sum the TotalAmount column and keep one of the rows based on the content of the Adjustment column. If the content of the Adjustment cell for an employee has a 2 for one row and an 8 for the second record, then keep sum the TotalAmount and keep the row with 2. If they have 8 and 11 then keep 8. If 7 and 8 then keep 8.
The result should look as follows:
You could use:
Select EmployeeID, StartDate, Max(Adjustment), Sum(TotalAmount)
From YourTable
Group By EmployeeID, StartDate
But that will not return 8 for ID 3 and 4. There is no logic telling why 8 should be returned from these IDs.
I've created an item history table for which I'd like to automatically increment VersionId partitioned by ItemId.
ID ItemId VersionId ItemContent Created
1 1 1 fooo 2015-02-24 12:54:00.11
2 2 1 barr 2015-02-24 12:54:15.35
3 1 2 foo 2015-02-24 12:55:00.61
4 1 3 baz 2015-02-24 12:55:45.23
5 2 2 bar 2015-02-24 12:56:00.03
Currently, that VersionId is set in a trigger. What I would like to find is something like this (I know this doesn't actually work):
create table ItemHistory
(
Id int identity(1,1) not null primary key
,ItemId int not null references Item(Id)
,VersionId int not null default row_number() over (partition by ItemId order by Id)
,ItemContent varchar(max)
,Created datetime not null default getdate()
)
While I have a working solution, I'd like to know if I'm missing some built-in functionality for handling this case.
In case you want to use my comment as the answer
Why not just do it with a view? You can have row_number() in a view.
With an index on ItemId, Id it should be very fast.
Probably less overhead than trigger.
I see you added a comment read only. Even more reason for view.
I've deleted some records (more precisely row 4) from a table in a SQL Server database. Now the first column goes like this (1,2,3,5) without row 4:
ID Name
------------
1 Luk
2 Sky
3 Philips
5 Andrey
How can I recreate this table and insert all data again in appropriate order?
Like this:
ID Name
--------
1 Luk
2 Sky
3 Philips
4 Andrey
EDIT:
But if i have another column (number) that is not a key, like this:
ID Number Name
------------
1 1 Luk
2 2 Sky
3 3 Philips
5 5 Andrey
Then can i recreate column Number and Name,
ID Number Name
------------
1 1 Luk
2 2 Sky
3 3 Philips
5 4 Andrey 'Can i do this, and if can HOW?
I would make a pretty strong case for never storing this number, since it is calculated, instead you could just create a view:
CREATE VIEW dbo.YourView
AS
SELECT ID,
Number = ROW_NUMBER() OVER(ORDER BY ID),
Name
FROM dbo.YourTable;
GO
This way after you have deleted rows, your view will already be in sync without having to perform any updates.
If you need to store the value, then almost the same query applies, but just placed inside a common table expression, which is then updated:
WITH CTE AS
( SELECT ID,
Number,
NewNumber = ROW_NUMBER() OVER(ORDER BY ID)
FROM dbo.YourTable
)
UPDATE CTE
SET Number = NewNumber;
You can use dbcc command
DBCC CHECKIDENT('tableName', RESEED, 0)
It would reset identity to 0.
Note it would require to truncate table first.
You can make the ID to auto increment which by default, the starting value for AUTO_INCREMENT is 1, and it will increment by 1 for each new record.
E.g MSSQL uses IDENTITY keyword to auto increment whereas MySQL uses the AUTO_INCREMENT keyword to perform an auto-increment feature.
MSSQL
ID int IDENTITY(1,1) PRIMARY KEY
MySQL
ID int NOT NULL AUTO_INCREMENT