Incrementing value based on criteria in a single database query - sql

I have a table which has a column labeled 'sortorder' which is used to allow customer to manually change the order of each item. The table also has a column labeled 'CategoryId'.
I was curious, if I was bulk importing a set of data in which I knew all data, including CategoryId, how I could specify the incrimenting value for 'SortOrder' inside the query, so that it went from 1 to X within each unique CategoryId.
Thanks everyone.

I'm not sure I understand your question but I think what you're asking is how to synthesize an appropriate SortOrder during an insert into the table. You should use ROW_NUMBER() with partitioning by CategoryId. Of course you will need to define a sorting criteria that gives the propert order of '1 to X':
INSERT INTO myTable (SortOrder, CategoryId, <other columns> ...)
SELECT ROW_NUMBER() OVER (PARTITION BY CategoryId ORDER BY mySortCriteria)
, CategoryId
, <other columns> ...
FROM SourceTable;

Sounds like you're needing to use the row_number function in your import.
INSERT MyTable(SortOrder, ...)
SELECT SortOrder = row_number() over (partition by CatgoryID order by SomeOtherField), ...
FROM MyTable

For anyone who might come along later who is still in SQL Server 2000, here is another way to accomplish the task.
Using a staging table of some sort to fix up the data from the bulk insert. Make sure it has a column called sortorder which is initally populated with a 1 and an identity based id column.
then you can update this with a self join, something like
update t
set sortorder = t1.sortorder +1
from test t
join Test t1 on t.id = t1.id+1
Then put the data into your prod table.

Related

SQL call Max row number from a temp table

In the temp table there are only two columns available. I would like to get the most recent ID for each load, as shown in the picture below.
I have tried this but it doesn't give me the answer I need.
select max(rn_plus_1),a.load, a.id from( select a.load,
a.id,
ROW_NUMBER() over(order by a.id desc) rn from max_num a group by load
, id) a
TEMP_TABLE lacks a sequential primary key or any other indicator for order of insertion. So it is not possible to get the latest ID for a LOAD using the columns of the table itself.
However, there is one option: ORA_ROWSCN(). This is a pseudo-column which identifies the System Change Number for the transaction which changed the table. So we can reconstruct the order of insertion by sorting the table on ORA_ROWSCN.
There are some caveats:
By default the SCN applies to the block level. Consequently all the rows in a block have the same SCN. It's a good enough approximation for wide tables but hopeless for a two-column toy like TEMP_TABLE. We can track SCN at the row level but only if the table is created with ROWDEPENDENCIES. The default is NOROWDEPENDENCIES. Unfortunately, we cannot use ALTER TABLE here. You will need to drop and recreate the table (*) to enable ROWDEPENDENCIES.
The SCN applies to the transaction. This means the solution will only work if each row in TEMP_TABLE is inserted in a separate transaction.
Obviously this is only possible if TEMP_TABLE is an actual table and not a view or some other construct.
Given all these criteria are satisfied here is a query which will give you the result set you want:
select load, id
from ( select load
, id
, row_number() over (partition by load order by ora_rowscn desc) as rn
from temp_table
)
where rn = 1
There is a demo on db<>fiddle. Also, the same demo except TEMP_TABLE defined with NOROWDEPENDENCIES, which produces the wrong result.
(*) If you need to keep the data in TEMP_TABLE the steps are:
rename TEMP_TABLE to whatever;
create table TEMP_TABLE as select * from whatever rowdependencies;
drop table whatever;
However, the SCN will be the same for the existing rows. If that matters you'll have to insert each row one at a time, in the order you wish to preserve, and commit after each insert.

SQL Eliminate Duplicates with NO ID

I have a table with the following Columns...
Node, Date_Time, Market, Price
I would like to delete all but 1 record for each Node, Date time.
SELECT Node, Date_Time, MAX(Price)
FROM Hourly_Data
Group BY Node, Date_Time
That gets the results I would like to see but cant figure out how to remove the other records.
Note - There is no ID for this table
Here are steps that are rather workaround than a simple one-command which will work in any relational database:
Create new table that looks just like the one you already have
Insert the data computed by your group-by query to newly created table
Drop the old table
Rename new table to the name the old one used to have
Just remember that locking takes place and you need to have some maintenance time to perform this action.
There are simpler ways to achieve this, but they are DBMS specific.
here is an easy sql-server method that creates a Row Number within a cte and deletes from it. I believe this method also works for most RDBMS that support window functions and Common Table Expressions.
;WITH cte AS (
SELECT
*
,RowNum = ROW_NUMBER() OVER (PARTITION BY Node, Date_Time ORDER BY Price DESC)
FROM
Hourly_Data
)
DELETE
FROM
cte
WHERE
RowNum > 1

Re-indexing a column with either SQL or PL/SQL

I have several tables that use an ID number plus a column called xsequence that are both primary keys. Currently, I have a bunch of data that looks like this:
ID_NUMBER,XSEQUENCE
001,2
001,5
001,8
002,1
002,6
What I need to end up with is:
ID_NUMBER,XSEQUENCE
001,1
001,2
001,3
002,1
002,2
What is the best way of going about starting this? Every time I try, I just end up spinning my wheels.
Try something like this:
select id_number,
row_number() over (partition by id_number order by xsequence) new_xsequence
from yourtable
That's an analytic function really handy for this sort of thing. Using the Partition keyword - "resets" the counter at each id_number. (so 1,2,3 .. then starts again 1,2,3 ... etc.).
(The Partition keyword in analytic functions behaves very similar to the GROUP by keyword)
[edit]
To UPDATE the original table, I actually prefer the MERGE statement - it's a bit simpler syntax wise, and seems a bit more intuitive ;) )
MERGE INTO yourtable base
USING (
select rowid rid,
id_number,
row_number() over (partition by id_number order by xsequence) new_xsequence,
xsequence old_xsequence
from yourtable
) new
ON ( base.rowid = new.rid )
WHEN MATCHED THEN UPDATE
SET base.xsequence = new.new_xsequence
[edit]

My tricky SQL Update query not working so well

I am trying to update a table in my database with another row from another table. I have two parameters one being the ID and another being the row number (as you can select which row you want from the GUI)
this part of the code works fine, this returns one column of a single row.
(SELECT txtPageContent
FROM (select *, Row_Number() OVER (ORDER BY ArchiveDate asc) as rowid
from ARC_Content Where ContentID = #ContentID) as test
Where rowid = #rowID)
its just when i try to add the update/set it won't work. I am probably missing something
UPDATE TBL_Content
Set TBL_Content.txtPageContent = (select txtPageContent
FROM (select *, Row_Number() OVER (ORDER BY ArchiveDate asc) as rowid
from ARC_Content Where ContentID = #ContentID) as test
Where rowid = #rowID)
Thanks for the help! (i have tried top 1 with no avail)
I see a few issues with your update. First, I don't see any joining or selection criteria for the table that you're updating. That means that every row in the table will be updated with this new value. Is that really what you want?
Second, the row number between what is on the GUI and what you get back in the database may not match. Even if you reproduce the query used to create your list in the GUI (which is dangerous anyway, since it involves keeping the update and the select code always in sync), it's possible that someone could insert or delete or update a row between the time that you fill your list box and send that row number to the server for the update. It's MUCH better to use PKs (probably IDs in your case) to determine which row to use for updating.
That said, I think that the following will work for you (untested):
;WITH cte AS (
SELECT
txtPageContent,
ROW_NUMBER() OVER (ORDER BY ArchiveDate ASC) AS rowid
FROM
ARC_Content
WHERE
ContentID = #ContentID)
UPDATE
TC
SET
txtPageContent = cte.txtPageContent
FROM
TBL_Content TC
INNER JOIN cte ON
rowid = #rowID

SQLServer SQL query with a row counter

I have a SQL query, that returns a set of rows:
SELECT id, name FROM users where group = 2
I need to also include a column that has an incrementing integer value, so the first row needs to have a 1 in the counter column, the second a 2, the third a 3 etc
The query shown here is just a simplified example, in reality the query could be arbitrarily complex, with several joins and nested queries.
I know this could be achieved using a temporary table with an autonumber field, but is there a way of doing it within the query itself ?
For starters, something along the lines of:
SELECT my_first_column, my_second_column,
ROW_NUMBER() OVER (ORDER BY my_order_column) AS Row_Counter
FROM my_table
However, it's important to note that the ROW_NUMBER() OVER (ORDER BY ...) construct only determines the values of Row_Counter, it doesn't guarantee the ordering of the results.
Unless the SELECT itself has an explicit ORDER BY clause, the results could be returned in any order, dependent on how SQL Server decides to optimise the query. (See this article for more info.)
The only way to guarantee that the results will always be returned in Row_Counter order is to apply exactly the same ordering to both the SELECT and the ROW_NUMBER():
SELECT my_first_column, my_second_column,
ROW_NUMBER() OVER (ORDER BY my_order_column) AS Row_Counter
FROM my_table
ORDER BY my_order_column -- exact copy of the ordering used for Row_Counter
The above pattern will always return results in the correct order and works well for simple queries, but what about an "arbitrarily complex" query with perhaps dozens of expressions in the ORDER BY clause? In those situations I prefer something like this instead:
SELECT t.*
FROM
(
SELECT my_first_column, my_second_column,
ROW_NUMBER() OVER (ORDER BY ...) AS Row_Counter -- complex ordering
FROM my_table
) AS t
ORDER BY t.Row_Counter
Using a nested query means that there's no need to duplicate the complicated ORDER BY clause, which means less clutter and easier maintenance. The outer ORDER BY t.Row_Counter also makes the intent of the query much clearer to your fellow developers.
In SQL Server 2005 and up, you can use the ROW_NUMBER() function, which has options for the sort order and the groups over which the counts are done (and reset).
The simplest way is to use a variable row counter. However it would be two actual SQL commands. One to set the variable, and then the query as follows:
SET #n=0;
SELECT #n:=#n+1, a.* FROM tablename a
Your query can be as complex as you like with joins etc. I usually make this a stored procedure. You can have all kinds of fun with the variable, even use it to calculate against field values. The key is the :=
Heres a different approach.
If you have several tables of data that are not joinable, or you for some reason dont want to count all the rows at the same time but you still want them to be part off the same rowcount, you can create a table that does the job for you.
Example:
create table #test (
rowcounter int identity,
invoicenumber varchar(30)
)
insert into #test(invoicenumber) select [column] from [Table1]
insert into #test(invoicenumber) select [column] from [Table2]
insert into #test(invoicenumber) select [column] from [Table3]
select * from #test
drop table #test