edit and Update records using reference id - sql

i have table with multiple records in a field name Comments... with my aspx code the data in comments column gets inserted in three rows with different requirementcommentid but the field comment will remain same
to retrieve distinct i used this query
SELECT distinct (
select top 1 requirementcommentid
from Requirementcomment
where requirementcomment=rc.requirementcomment
and fcr.SectionID in(
SELECT sectionid
FROM [dbo].udfGetSectionID_allComYear(2151)
)
AND fcr.FirmID = 20057
),
rc.IsRejected,
fcr.SectionID,
rc.UserID,
rc.RequirementComment,
convert(varchar(25), dateadd(hour, -5, rc.InsertDate),101) as InsertDate,
Department.DeptName,
FirmUser.DepartmentID,
rc.FirmComplianceYearID
FROM RequirementComment rc
INNER JOIN FirmComplianceRequirement fcr ON fcr.FirmComplianceRequirementID = rc.FirmComplianceRequirementID
INNER JOIN FirmUser ON FirmUser.FirmUserID =rc.UserID
INNER JOIN Department ON Department.DeptID = FirmUser.DepartmentID WHERE rc.IsRejected = 1
AND fcr.SectionID in(SELECT sectionid FROM [dbo].udfGetSectionID_allComYear (2151))
AND fcr.FirmID = 20057 AND rc.RequirementComment!=''
if i want to edit this distinct comment and update it.how can i do this... as only one comment row get edited remaining two rows value in field comment remain the same...!
i want remaining data to be updated automatically if i clicked on edit and updated only single record

If you can not solve this with a procedure when storing, or in .NET, consider to use a trigger. I have made a generic example, since your example code is a bit complex :)
CREATE TABLE TMP_TriggerTable
(
ID INT IDENTITY(1,1) PRIMARY KEY
, ID2 INT NOT NULL
, Comment VARCHAR(255) NOT NULL
)
GO
INSERT INTO TMP_TriggerTable
SELECT 1, 'asd'
UNION ALL
SELECT 1, 'asd'
UNION ALL
SELECT 1, 'asd'
UNION ALL
SELECT 2, 'asd'
UNION ALL
SELECT 2, 'asd'
UNION ALL
SELECT 2, 'asd'
GO
CREATE TRIGGER TRG_TMP_TriggerTable ON TMP_TriggerTable
AFTER UPDATE
AS
BEGIN
WITH InsertedIDPriority AS
(
--Handle if more than one related comment was updated
SELECT Prio = ROW_NUMBER() OVER (PARTITION BY ID2 ORDER BY ID)
, ID
, ID2
, Comment
FROM INSERTED
)
UPDATE t SET Comment = i.Comment FROM TMP_TriggerTable t
JOIN InsertedIDPriority i ON
t.ID2 = i.ID2 --Select all related comments
AND t.ID != i.ID2 --No need to update main column two times
AND i.Prio = 1 --Handle if more than one related comment was updated
END
GO
UPDATE TMP_TriggerTable SET Comment = 'asd2' WHERE ID = 1
/*
SELECT * FROM TMP_TriggerTable
--Returns--
ID ID2 Comment
1 1 asd2
2 1 asd2
3 1 asd2
4 2 asd
5 2 asd
6 2 asd
*/

Related

Where clause between union all in sql?

I have a query that vertically expands data by using Union condition. Below are the 2 sample tables:
create table #temp1(_row_ord int,CID int,_data varchar(10))
insert #temp1
values
(1,1001,'text1'),
(2,1001,'text2'),
(4,1002,'text1'),
(5,1002,'text2')
create table #temp2(_row_ord int,CID int,_data varchar(10))
insert #temp2
values
(1,1001,'sample1'),
(2,1001,'sample2'),
(4,1002,'sample1'),
(5,1002,'sample2')
--My query
select * from #temp1
union
select * from #temp2 where CID in (select CID from #temp1)
order by _row_ord,CID
drop table #temp1,#temp2
So my current output is:
I want to group the details of every client together for which I am unable to use 'where' clause across Union condition.
My desired output:
Any help?! Order by is also not helping me.
I can imagine you want all of the rows for a CID sorted by _row_ord from the first table before the ones from the second table. And the CID should be the outermost sort criteria.
If that's right, you can select literals from your tables. Let the literal for the first table be less than that of the second table. Then first sort by CID, then that literal and finally by _row_ord.
SELECT cid,
_data
FROM (SELECT 1 s,
_row_ord,
cid,
_data
FROM #temp1
UNION ALL
SELECT 2 s,
_row_ord,
cid,
_data
FROM #temp2) x
ORDER BY cid,
s,
_row_ord;
db<>fiddle
If I correctly understand your need, you need the output to be sorted the way that #temp1 rows appear before #temp2 rows for each cid value.
What you could do is generate additional column ordnum assigning values for each table, just for sorting purposes, and then get rid of it in the outer select statement.
select cid, _data
from (
select 1 as ordnum, *
from #temp1
union all
select 2 as ordnum, *
from #temp2 t2
where exists (
select 1
from #temp1 t1
where t1.cid = t2.cid
)
) q
order by cid, ordnum
I have also rewritten your where condition for an equivalent which should work faster using exists operator.
Live DEMO - click me!
Output
cid _data
1001 text1
1001 text2
1001 sample1
1001 sample2
1002 text1
1002 text2
1002 sample1
1002 sample2
Use With. here is my first try with your sql
create table #temp1(_row_ord int,CID int,_data varchar(10))
insert #temp1
values
(1,1001,'text1'),
(2,1001,'text2'),
(4,1002,'text1'),
(5,1002,'text2')
create table #temp2(_row_ord int,CID int,_data varchar(10))
insert #temp2
values
(1,1001,'sample1'),
(2,1001,'sample2'),
(4,1002,'sample1'),
(5,1002,'sample2');
WITH result( _row_ord, CID,_data) AS
(
--My query
select * from #temp1
union
select * from #temp2 where CID in (select CID from #temp1)
)
select * from tmp order by CID ,_data
drop table #temp1,#temp2
result
_row_ord CID _data
1 1001 sample1
2 1001 sample2
1 1001 text1
2 1001 text2
4 1002 sample1
5 1002 sample2
4 1002 text1
5 1002 text2
Union is placed between two result set blocks and forms a single result set block. If you want a where clause on a particular block you can put it:
select a from a where a = 1
union
select z from z
select a from a
union
select z from z where z = 1
select a from a where a = 1
union
select z from z where z = 1
The first query in a union defines column names in the output. You can wrap an output in brackets, alias it and do a where on the whole lot:
select * from
(
select a as newname from a where a = 1
union
select z from z where z = 2
) o
where o.newname = 3
It is important to note that a.a and z.z will combine into a new column, o.newname. As a result, saying where o.newname will filter on all rows from both a and z (the rows from z are also stacked into the newname column). The outer query knows only about o.newname, it knows nothing of a or z
Side note, the query above produces nothing because we know that only rows where a.a is 1 and z.z is 2 are output by the union as o.newname. This o.newname is then filtered to only output rows that are 3, but no rows are 3
select * from
(
select a as newname from a
union
select z from z
) o
where o.newname = 3
This query will pick up any rows in a or z where a.a is 3 or z.z is 3, thanks to the filtering of the resulting union

Linking together different columns in a SQL table?

I'm not very experienced with advance SQL queries, I'm familiar with basic statements and basic joins, currently trying to figure out how to write a query that seems to be out of my depth and I haven't been able to find a solution from google so far and I'm hoping somebody might be able to point me in the right direction.
The table I'm working with has an ID column, and a 'parent id' column.
I'm looking for all descendants of ID '1' - rows with a parent ID of '1', rows with a parent ID equal to any row's ID with a parent ID of '1' etc. Currently I've been doing this manually but there are hundreds of descendants so far and I feel like there's a way to put this into one query.
Any help would be appreciated, if this is unclear I can also try to clarify.
EDIT - I got it working with the following query:
with cteMappings as (
select map_id, parent_map_id, map_name
from admin_map
where map_id = '1'
union all
select a.map_id, a.parent_map_id, a.map_name
from admin_map a
inner join cteMappings m
on a.parent_map_id = m.map_id
)
select map_id, parent_map_id, map_name
from cteMappings
Sounds like it can be achieved by Common Table Expression:
DECLARE #temp TABLE (id INT IDENTITY(1, 1), parent_id INT);
INSERT #temp
SELECT NULL
UNION ALL
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT NULL
SELECT * FROM #temp
; WITH HierarchyTemp (id, parent_id, depth) AS (
SELECT id, parent_id, 0
FROM #temp
WHERE id = 1
UNION ALL
SELECT t.id, t.parent_id, ht.depth + 1
FROM #temp t
INNER JOIN HierarchyTemp ht ON ht.id = t.parent_id
)
SELECT *
FROM HierarchyTemp
So the above example is creating a table variable with 4 rows:
id parent_id
1 NULL
2 1
3 2
4 NULL
Result of descendent of id '1' (also including itself, but can be excluded with an additional WHERE clause):
id parent_id depth
1 NULL 0
2 1 1
3 2 2

Fixing duplicate rows in a table

I have a table like below
DECLARE #ProductTotals TABLE
(
id int,
value nvarchar(50)
)
which has following value
1, 'abc'
2, 'abc'
1, 'abc'
3, 'abc'
I want to update this table so that it has the following values
1, 'abc'
2, 'abc_1'
1, 'abc'
3, 'abc_2'
Could someone help me out with this
Use a cursor to move over the table and try to insert every row in a second temporary table. If you get a collision (technically with a select), you can run a second query to get the maximum number (if any) that's appended to your item.
Once you know what maximum number is used (use isnull to cover the case of the first duplicate) just run an update over your original table and keep going with your scan.
Are you looking to remove duplicates? or just change the values so they aren't duplicate?
to change the values use
update producttotals
set value = 'abc_1'
where id =2;
update producttotals
set value = 'abc_2'
where id =3;
to find duplicate rows do a
select id, value
from producttotals
group by id, value
having count() > 2;
Assuming SQL Server 2005 or greater
DECLARE #ProductTotals TABLE
(
id int,
value nvarchar(50)
)
INSERT INTO #ProductTotals
VALUES (1, 'abc'),
(2, 'abc'),
(1, 'abc'),
(3, 'abc')
;WITH CTE as
(SELECT
ROW_NUMBER() OVER (Partition by value order by id) rn,
id,
value
FROM
#ProductTotals),
new_values as (
SELECT
pt.id,
pt.value,
pt.value + '_' + CAST( ROW_NUMBER() OVER (partition by pt.value order by pt.id) as varchar) new_value
FROM
#ProductTotals pt
INNER JOIN CTE
ON pt.id = CTE.id
and pt.value = CTE.value
WHERE
pt.id NOT IN (SELECT id FROM CTE WHERE rn = 1)) --remove any with the lowest ID for the value
UPDATE
#ProductTotals
SET
pt.value = nv.new_value
FROM
#ProductTotals pt
inner join new_values nv
ON pt.id = nv.id and pt.value = nv.value
SELECT * FROM #ProductTotals
Will produce the following
id value
----------- --------------------------------------------------
1 abc
2 abc_1
1 abc
3 abc_2
Explanation of the SQL
The first CTE creates a row number Value. So the numbering gets restarted whenever it sees a new value
rn id value
-------------------- ----------- --------
1 1 abc
2 1 abc
3 2 abc
4 3 abc
The second CTE called new_values ignores any IDs that are assoicated with with a RN of 1. So rn 1 and rn 2 get removed because they share the same ID. It also uses ROW_NUMBER() again to determine the number for the new_value
id value new_value
----------- ------ -------------
2 abc abc_1
3 abc abc_2
The final statement just updates the Old value with the new value

Simple SQL: How to calculate unique, contiguous numbers for duplicates in a set?

Let's say I create a table with an int Page, int Section, and an int ID identity field, where the page field ranges from 1 to 8 and the section field ranges from 1 to 30 for each page. Now let's say that two records have duplicate page and section. How could I renumber those two records so that the sequence of page and section numbering is contiguous?
select page, section
from #fun
group by page, section having count(*) > 1
shows the duplicates:
page 1 section 3
page 2 section 3
page 1 section 4 and page 2 section 4 are missing. Is there a way without using a cursor to find and renumber the positions in SQL 2000 that doesn't support Row_Number()?
This rownum below of course produces exactly the same number as in section:
select page, section,
(select count(*) + 1
from #fun b
where b.page = a.page and b.section < a.section) as rownum
from #fun a
I could create a pivot table having values 1 through 100, but what would I join against?
What I want to do is something like this:
update p set section = (expression that gets 4)
from #fun p
where (expression that identifies duplicate sections by page)
I don't have a 2000 server to test this on, but I think it should work.
Create test tables/data:
CREATE TABLE #fun
(Id INT IDENTITY(100,1)
,page INT NOT NULL
,section INT NOT NULL
)
INSERT #fun (page, section)
SELECT 1,1
UNION ALL SELECT 1,3 UNION ALL SELECT 1,2
UNION ALL SELECT 1,3 UNION ALL SELECT 1,5
UNION ALL SELECT 2,1 UNION ALL SELECT 2,2
UNION ALL SELECT 2,3 UNION ALL SELECT 2,5
UNION ALL SELECT 2,3
Now the processing:
-- create a worktable
CREATE TABLE #fun2
(Id INT IDENTITY(1,1)
,funId INT
,page INT NOT NULL
,section INT NOT NULL
)
-- insert data into the second temp table ordered by the relevant columns
-- the identity column will form the basis of the revised section number
INSERT #fun2 (funId, page, section)
SELECT Id,page,section
FROM #fun
ORDER BY page,section,Id
-- write the calculated section value back where it is different
UPDATE p
SET section = y.calc_section
FROM #fun AS p
JOIN
(
SELECT f2.funId, f2.id - x.adjust calc_section
FROM #fun2 AS f2
JOIN (
-- this subquery is used to calculate an offset like
-- PARTITION BY in a 2005+ ROWNUMBER function
SELECT MIN(Id) - 1 adjust, page
FROM #fun2
GROUP BY page
) AS x
ON f2.page = x.page
) AS y
ON p.Id = y.funId
WHERE p.section <> y.calc_section
SELECT * FROM #fun order by page, section
Disclaimer: I don't have SQL Server to test.
If I understand you correctly, if you knew the ROW_NUMBER of your #fun records partitioned over (page, section) duplicates, you could use this relative ranking to increment the "section":
UPDATE p
SET section = section + (rownumber - 1)
FROM #fun AS p
INNER JOIN ( -- SELECT id, ROW_NUMBER() OVER (PARTITION BY page, section) ...
SELECT id, COUNT(1) AS rownumber
FROM #fun a
LEFT JOIN #fun b
ON a.page = b.page AND a.section = b.section AND a.id <= b.id
GROUP BY a.id, a.page, a.section) d
ON p.id = d.id
WHERE rownumber > 1
That won't handle the case where the number of duplicates push you past your upper limit of 30. It may also create new duplicates where if higher numbered sections per page already exist -- that is, one instance of (pg 1, sec 3) becomes (pg 1, sec 4), which already existed -- but you can run the UPDATE repeatedly until no duplicates exist.
And then add a unique index on (page, section).

How do you find a missing number in a table field starting from a parameter and incrementing sequentially?

Let's say I have an sql server table:
NumberTaken CompanyName
2 Fred 3 Fred 4 Fred 6 Fred 7 Fred 8 Fred 11 Fred
I need an efficient way to pass in a parameter [StartingNumber] and to count from [StartingNumber] sequentially until I find a number that is missing.
For example notice that 1, 5, 9 and 10 are missing from the table.
If I supplied the parameter [StartingNumber] = 1, it would check to see if 1 exists, if it does it would check to see if 2 exists and so on and so forth so 1 would be returned here.
If [StartNumber] = 6 the function would return 9.
In c# pseudo code it would basically be:
int ctr = [StartingNumber]
while([SELECT NumberTaken FROM tblNumbers Where NumberTaken = ctr] != null)
ctr++;
return ctr;
The problem with that code is that is seems really inefficient if there are thousands of numbers in the table. Also, I can write it in c# code or in a stored procedure whichever is more efficient.
Thanks for the help
Fine, if this question isn't going to be closed, I may as well Copy and paste my answer from the other one:
I called my table Blank, and used the following:
declare #StartOffset int = 2
; With Missing as (
select #StartOffset as N where not exists(select * from Blank where ID = #StartOffset)
), Sequence as (
select #StartOffset as N from Blank where ID = #StartOffset
union all
select b.ID from Blank b inner join Sequence s on b.ID = s.N + 1
)
select COALESCE((select N from Missing),(select MAX(N)+1 from Sequence))
You basically have two cases - either your starting value is missing (so the Missing CTE will contain one row), or it's present, so you count forwards using a recursive CTE (Sequence), and take the max from that and add 1
Tables:
create table Blank (
ID int not null,
Name varchar(20) not null
)
insert into Blank(ID,Name)
select 2 ,'Fred' union all
select 3 ,'Fred' union all
select 4 ,'Fred' union all
select 6 ,'Fred' union all
select 7 ,'Fred' union all
select 8 ,'Fred' union all
select 11 ,'Fred'
go
I would create a temp table containing all numbers from StartingNumber to EndNumber and LEFT JOIN to it to receive the list of rows not contained in the temp table.
If NumberTaken is indexed you could do it with a join on the same table:
select T.NumberTaken -1 as MISSING_NUMBER
from myTable T
left outer join myTable T1
on T.NumberTaken= T1.NumberTaken+1
where T1.NumberTaken is null and t.NumberTaken >= STARTING_NUMBER
order by T.NumberTaken
EDIT
Edited to get 1 too
1> select 1+ID as ID from #b as b
where not exists (select 1 from #b where ID = 1+b.ID)
2> go
ID
-----------
5
9
12
Take max(1+ID) and/or add your starting value to the where clause, depending on what you actually want.