TSQL Field Order Increment during Insert - sql-server

I have a simple query that is taking in an XML string and inserting one or more rows of data depending on what was selected on the UI.
There is a field on the records that determine their order and by default, these new fields are added to the end of the table.
If 3 records exist with the OrderID of 1,2,3 respectively and I add 3 more records through the XML string, those records should get 4,5,6 as their orderID. It doesn't matter which 3 values in the new set get which order, just that they continue the incrementation.
Here was my attempt, which works fine if there is a single item but if there are multiple, #newOrder is always the same so the count is invalid with #newOrder+1.
-- Get the next order based on the last entry
DECLARE #newOrder INT = (SELECT TOP 1 DepartmentOrder FROM c.department WHERE dID = #dID ORDER BY DepartmentOrder DESC)
-- Add new department
INSERT INTO c.department (dID,DepartmentID,DepartmentOrder)
SELECT #dID,
ParamValues.x1.value('departmentID[1]', 'int'),
ISNULL(#newOrder,0)+1
FROM #departments.nodes('/departments/department') AS ParamValues(x1)
Any thoughts?

You can use ROW_NUMBER to generate the new values:
INSERT INTO c.department (dID,DepartmentID,DepartmentOrder)
SELECT #dID,
ParamValues.x1.value('departmentID[1]', 'int'),
ISNULL(#newOrder,0)+ROW_NUMBER() OVER (ORDER BY ParamValues.x1)
FROM #departments.nodes('/departments/department') AS ParamValues(x1)
By using the XML column x1 in the ORDER BY clause, you are specifying that the DepartmentOrder should be ordered the same way the nodes appear in the xml.

Related

Talend: Get most common value in a column

I have a table with a couple hundred rows. I want to know the most common value of the data in one of the columns. How do I go about that?
I recommend you do it in your sql query with something like this :
select top 1 column, count(*) cnt
from table
group by column
order by count(*) desc
This syntax has to be adapted to your rdbms. For instance, in Oracle it would be something like this :
select column from (
select column, count(*)
from table
group by column
order by count(*) desc
) where rownum = 1
If you want to do it in Talend you can use :
Input -- tAggregateRow -- tSortRow -- tSampleRow -- Output
In tAggregateRow you use a count function to count the frequency of values in your column, then you sort them by descending order in tSortRow, then you get the first line with tSampleRow (just put "1")

Distinct valĂșes while using multiple joins

I want to obtain one single row but it is returning 3 rows. The form_audit table has 3 rows with the same REF_NO,
How to get one distinct row?
Hope this will give you some idea how it works
CREATE TABLE #tblBackers (
amountBacked MONEY,
backersAccountID INT,
playerBacked INT,
Dates DATETIME)
INSERT INTO #tblBackers VALUES (25,12345,99999,GETDATE())
INSERT INTO #tblBackers VALUES (25,12345,99999,GETDATE())
INSERT INTO #tblBackers VALUES (25,12345,99699,GETDATE())
INSERT INTO #tblBackers VALUES (25,12345,99999,GETDATE())
INSERT INTO #tblBackers VALUES (25,98765,88888,GETDATE())
INSERT INTO #tblBackers VALUES (25,76543,77777,GETDATE())
GO
SELECT DISTINCT * FROM #tblBackers
SELECT DISTINCT TOP 1 * FROM #tblBackers
And use the ORDER BY to get the latest record.
If you only want one record per ref_no, then consider adding a group by clause on that field.
select
fa.ref_no
/*, other stuff*/
from
FORM_AUDIT fa
/* other joins*/
group by
fa.ref_no;
Keep in mind that this group by clause will aggregate all records that share the same ref_no into a single record in the result set. That means that you can no longer include fields like fh.* and fcd.* in the select list directly, because you have no guarantee that each of those fields has only one value per row in your result set. For every such field that you want to include in your select list, you must either:
Include that field in your group by clause, keeping in mind that doing so will no longer necessarily give you exactly one row per distinct ref_no; now you'll get one row per distinct combination of ref_no and whatever else you add to the group by clause, or
Use one of SQL Server's aggregate functions to transform the set of zero-to-many values in the field you're adding into a single value. Aggregate functions are things like max(), sum(), count(), etc. There's a complete list at the link.
Good luck!

TSQL Incrementing Count of Variable

I have a UI that allows a user to select one or more fields they want to add to a table. This data also has an orderID associated with it that determines the field order.
When the user adds new fields, I need to find the last orderID this user used and increment it by 1, submitting all of the new fields.
For example, if there is a single record that already exists in the database, it would have an orderID of 1. When I choose to add three more fields, it would check to see the last orderID I used (1) and then increment it for each of the new records it adds, 1-4.
-- Get the last ID orderID for this user and increment it by 1 as our starting point
DECLARE #lastID INT = (SELECT TOP 1 orderID FROM dbo.BS_ContentRequests_Tasks_User_Fields WHERE QID = #QID ORDER BY orderID DESC)
SET #lastID = #lastID+1;
-- Create a temp table to hold our fields that we are adding
DECLARE #temp AS TABLE (fieldID int, orderID int)
-- Insert our fields and incremented numbers
INSERT INTO #temp( fieldID, orderID )
SELECT ParamValues.x1.value('selected[1]', 'int'),
#lastID++
FROM #xml.nodes('/root/data/fields/field') AS ParamValues(x1);
Obviously the #lastID++ part is where my issue is but hopefully it helps to understand what I am trying to do.
What other method could be used to handle this?
ROW_NUMBER() ought to do it.
select x.Value,
ROW_NUMBER() over (order by x.Value) + #lastID
from (
select 10 ParamValues.x1.value('selected[1]', 'int') Value
from #xml.nodes('/root/data/fields/field') AS ParamValues(x1)
) x
You could use a column with IDENTITY(1,1)
If you want OrderID to be unique across the entire table then see below:
Click here to take a look at another post that addresses this issue.
There are multiple ways to approach this issue, but in this case, the easiest, while reasonable, means may be to use an identity column. However, that is not as extensible as using a sequence. If you feel that you may need more flexibility in the future, then use a sequence.
If you want OrderID to be unique across the fields inserted in one batch then see below:
You should take a closer look at Chris Steele's answer.

SQL - Looping - copy category

Assume Category table has categoryId, name and parentCategoryId columns, where categoryId is an identity field.
now what if I have to replicate a category and assign it to a new parent? Because it's not a simple insert statement. I have to insert all the subcategories and their subcategories and so on. How would I keep track of their identity fields? I would need that to assign parentCategoryId to their subcategories when inserting.
Is this question clear?
Not sure if you're asking 2 questions or just 1, i.e. replicating an entire category as a new category (i.e. copying a category) vs. re-assigning an existing category to a new parent - each would be different problems/solutions, but let's start with copying an entire category.
First, if you're using an identity-based column, the ONLY way you can do it without using the "set identity_insert on" option would be to cursor through the entire tree, starting from the root nodes and working down (i.e. insert the top-level category(ies), get the newly created identity values, insert the second-level categories, etc.). If you are in a scenario where you can make use of "set identity_insert on", or if you can replace the use of identities with explicit numbers, then you can leverage the code below.
In this code, you'll notice the use of CTE's, recursive CTE's, and ranking functions, so this assumes Sql 2005 or above. Also, the lvl, path, and cnt columns are simply included for demo purposes you can use to view if you like, not required in any final solution:
declare #root_category_id bigint,
#start_new_id_value bigint;
-- What category id do we want to move?
select #root_category_id = 3;
-- Get the current max id and pad a bit...
select #start_new_id_value = max(categoryId)
from Category;
select #start_new_id_value = coalesce(#start_new_id_value,0) + 100;
-- Show our values
select #root_category_id, #start_new_id_value;
begin tran;
set identity_insert Category on;
-- This query will give you the entire category tree
with subs (catId, parentCatId, catName, lvl, path, new_id, new_parent_id, cnt) as (
-- Anchor member returns a row for the input manager
select catId, parentCatId, catName, 0 as lvl,
cast(cast(catId as varchar(10)) as varchar(max)) as path,
#start_new_id_value + row_number() over(order by catId) - 1 as new_id,
cast(parentCatId as bigint) as new_parent_id,
count(*) over(partition by 0) as cnt
from Category
where catId = #root_category_id
union all
-- recursive member returns next level of children
select c.catId, c.parentCatId, c.catName, p.lvl + 1,
cast(p.path + '.' + cast(catId as varchar(10)) as varchar(max)),
p.cnt + row_number() over(order by c.catId) + p.new_id - 1 as new_id,
p.new_id as new_parent_id,
count(*) over(partition by p.lvl) as cnt
from subs as p -- Parent
join Category as c -- Child
on c.parentCatId = p.catId
)
-- Perform the insert
insert Category
(categoryId, Name, parentCategoryId)
select s.catId, s.catName, s.parentCatId
from subs s
--order by path;
set identity_insert Category off;
commit tran;
Interesting question.
If you're in SQL 2005+, I suppose you could build a CTE with the full tree of the category to replicate, which will put it in a temporary location to work with, away from the main table.
Then you can use a cursor to work your way down the tree and update the ID number of the parent ...
Now that I type it, it doesn't seem the most efficient solution. Perhaps you could instead do a fancy SELECT statement, which updates the ID of the parent ID as it's SELECTing?
Are you aware of nested sets? It's an alternative way of representing data in problems like this. I don't know if it would help here, but if you're not aware of it you might want to consider it. http://intelligent-enterprise.informationweek.com/001020/celko.jhtml

Ordering query result by list of values

I'm working on a sql query that is passed a list of values as a parameter, like
select *
from ProductGroups
where GroupID in (24,12,7,14,65)
This list is constructed of relations used througout the database, and must be kept in this order.
I would like to order the results by this list. I only need the first result, but it could be the one with GroupId 7 in this case.
I can't query like
order by (24,12,7,14,65).indexOf(GroupId)
Does anyone know how to do this?
Additional info:
Building a join works and running it in the mssql query editor, but...
Due to limitiations of the software sending the query to mssql, I have to pass it to some internal query builder as 1 parameter, thus "24,12,7,14,65". And I don't know upfront how many numbers there will be in this list, could be 2, could be 20.
You can also order by on a CASE:
select *
from ProductGroups
where GroupID in (24,12,7,14,65)
order by case GroupId
when 7 then 1 -- First in ordering
when 14 then 2 -- Second
else 3
end
Use a table variable or temporary table with an identity column, feed in your values and join to that, e.g.
declare #rank table (
ordering int identity(1,1)
, number int
)
insert into #rank values (24)
insert into #rank values (12)
insert into #rank values (7)
insert into #rank values (14)
insert into #rank values (65)
select pg.*
from ProductGroups pg
left outer join
#rank r
on pg.GroupId = r.number
order by
r.ordering
I think I might have found a possible solution (but it's ugly):
select *
from ProductGroups
where GroupID in (24,12,7,14,65)
order by charindex(
','+cast(GroupID as varchar)+',' ,
','+'24,12,7,14,65'+',')
this will order the rows by the position they occur in the list. And I can pass the string like I need too.
Do a join with a temporary table, in which you have the values that you want to filter by as rows. Add a column to it that has the order that you want as the second column, and sort by it.

Resources