I have the following table structure
| id | parentID | count1 |
2 -1 1
3 2 1
4 2 0
5 3 1
6 5 0
I increase count values from my source code, but i also need the increase in value to bubble up to each parent id row until the parent id is -1.
eg. If I were to increase count1 on row ID #6 by 1, row ID #5 would increase by 1, ID #3 would increase by 1, and ID #2 would increase by 1.
Rows also get deleted, and the opposite would need to happen, basically subtracting the row to be deleted' value from each parent.
Thanks in advance for your insight.
I'm using SQL Server 2008, and C# asp.net.
If you really want to just update counts, you could want to write stored procedure to do so:
create procedure usp_temp_update
(
#id int,
#value int = 1
)
as
begin
with cte as (
-- Take record
select t.id, t.parentid from temp as t where t.id = #id
union all
-- And all parents recursively
select t.id, t.parentid
from cte as c
inner join temp as t on t.id = c.parentid
)
update temp set
cnt = cnt + #value
where id in (select id from cte)
end
SQL FIDDLE EXAMPLE
So you could call it after you insert and delete rows. But if your count field are depends just on your table, I would suggest to make a triggers which will recalculate your values
You want to use a recursive CTE for this:
with cte as (
select id, id as parentid, 1 as level
from t
union all
select cte.id, t.parentid, cte.level + 1
from t join
cte
on t.id = cte.parentid
where cte.parentid <> -1
) --select parentid from cte where id = 6
update t
set count1 = count1 + 1
where id in (select parentid from cte where id = 6);
Here is the SQL Fiddle.
Related
I have a table bawe_services. i want to fetch all data that match with given keys
like i have fields
id | Service_id |bawe_id
1 2 2
2 3 3
3 2 3
if i pass service =2 i need all record of service_id=2 if i pass service=1,2,3 than i want 0 rows because 1 service is not given by any bawe so. i got 0 rows.
I use this query
select * from aspnet_bawe_services where ser_id in(1,2,3)
Thanx in advance
The count of the parameters in the "in" statement must match the having equal number.
select bawe_id from [dbo].[aspnet_bawe_services]
where Service_id in (2)
group by bawe_id
having count(Service_id)=1;
bawe_id
-----------
2
3
select bawe_id from [dbo].[aspnet_bawe_services]
where Service_id in (2,3)
group by bawe_id
having count(Service_id)=2;
bawe_id
-----------
3
select bawe_id from [dbo].[aspnet_bawe_services]
where Service_id in (1,2,3)
group by bawe_id
having count(Service_id)=3;
bawe_id
-----------
(0 row(s) affected)
TRY THIS: It's really tedious but unique requirement and I think to accomplish this, we have to use function
1-Function returns distinct count of service_id
2-Function to split comma separated value and return in table format
--Function returns distinct count of service_id
CREATE FUNCTION [dbo].[getCount](#service_id varchar(500))
RETURNS INT
AS
BEGIN
DECLARE #count int
SELECT #count = COUNT(DISTINCT(t.service_id))
FROM tmptos t
INNER JOIN [dbo].[SplitValue](#service_id, ',') tt on t.service_id = tt.items
RETURN #count
END;
--Function to split comma separated value and return in table format
--Function copied from
--separate comma separated values and store in table in sql server
CREATE FUNCTION [dbo].[SplitValue](#String varchar(MAX), #Delimiter char(1))
RETURNS #temptable TABLE (items VARCHAR(MAX))
AS
BEGIN
DECLARE #idx int
DECLARE #slice varchar(8000)
SELECT #idx = 1
if len(#String)<1 or #String is null return
WHILE #idx!= 0
BEGIN
set #idx = charindex(#Delimiter,#String)
IF #idx!=0
set #slice = left(#String,#idx - 1)
else
set #slice = #String
IF(LEN(#slice)>0)
INSERT INTO #temptable(Items) values(#slice)
SET #String = right(#String,len(#String) - #idx)
IF LEN(#String) = 0 break
END
RETURN
END;
--Table with Sample Data
create table tmptos(id int, Service_id int, bawe_id int)
insert into tmptos values
(1, 2, 2),
(2, 3, 3),
(3, 2, 3)
declare #service_id varchar(50) = '2,3'
select *
from tmptos t
inner join [dbo].[SplitValue](#service_id, ',') tt on t.Service_id = tt.items
where [dbo].[getCount](#service_id) = (select count(distinct(items)) from [dbo].[SplitValue](#service_id, ','))
OUTPUT:
id Service_id bawe_id items
1 2 2 2
2 3 3 3
3 2 3 2
It's bit lengthy but works perfectly.
select * from aspnet_bawe_services
where Service_id in (1,2,3)
and
( select count(distinct Service_id) from aspnet_bawe_services where Service_id in (1,2,3) ) = 3
last number in query (in this case "3") is elements count, which you have in IN list.
You can get the service ids that you want using group by and having:
select service_id
from t
where bawe_id in (1, 2, 3)
group by service_id
having count(distinct bawe_id) = 3;
The "= 3" is the number of ids in the IN list.
You can then use in or join or exists to get the full records:
select t.*
from t
where t.service_id in (select service_id
from t
where bawe_id in (1, 2, 3)
group by service_id
having count(distinct bawe_id) = 3
);
I need help regarding a SQL query problem. I have a query where I am able to delete the duplicates but I also need to create records of the duplicated data being deleted into a EventLog in which I am clueless about it. Below is an example of my Student Table. From the table below, you can see only Alpha and Bravo are duplicated
id Name Age Group
-----------------------
1 Alpha 11 A
2 Bravo 12 A
3 Alpha 11 B
4 Bravo 12 B
5 Delta 11 B
As I am copying data from Group A to Group B, I need to find & delete the duplicated data in group B. Below is my query on deleting duplicates from Group B.
DELETE Student WHERE id
IN (SELECT tb.id
FROM Student AS ta
JOIN Student AS tb ON ta.name=tb.name AND ta.age=tb.age
WHERE ta.GroupName='A' AND tb.GroupName='B')
Here is an example of my eventlog and how I want the query that I execute to like.
id Name Age Group Status
------------------------------------------
1 Alpha 11 B Delete
2 Bravo 11 B Delete
Instead of inserting the entire Group B data into the eventlog, is there any query that can just insert the Duplicated Data into the event log?
If we are speaking about Microsoft sql, key is output clause, more details here https://msdn.microsoft.com/en-us/library/ms177564.aspx
declare #Student table
( id int, name nvarchar(20), age int,"groupname" char(1))
insert into #student values (1, 'Alpha' , 11, 'A' ),
(2, 'Bravo' , 12, 'A'),
(3 ,'Alpha' , 11 , 'B'),
(4 ,'Bravo' ,12 , 'B'),
(5 ,'Delta' ,11 , 'B')
declare #Event table
( id int, name nvarchar(20), age int,"groupname" char(1),"Status" nvarchar(20))
select * from #Student
DELETE #Student
output deleted.*, 'Deleted' into #Event
WHERE id
IN (SELECT tb.id
FROM #Student AS ta
JOIN #Student AS tb ON ta.name=tb.name AND ta.age=tb.age
WHERE ta.GroupName='A' AND tb.GroupName='B')
select * from #event
Run this before the Delete above. Not sure how you decide what one is the duplicate but you can use Row_Number to list them with the non duplicate at as 1 and and then insert everything with a row_Number > 1
; WITH cte AS
(
SELECT Name
,Age
,[Group]
,STATUS = 'Delete'
,RID = ROW_NUMBER ( ) OVER ( PARTITION BY Name,Age ORDER BY Name)
FROM Student AS ta
JOIN Student AS tb ON ta.name=tb.name AND ta.age=tb.age
)
INSERT INTO EventLog
SELECT Name,Age,[Group],'Delete'
FROM cte
WHERE RID > 1
you need to create basic trigger after delete in student table, this query will be executed after any deletion process in student table and will insert deleted record into log_table
create trigger deleted_records
on student_table
after delete
as
begin
insert into log_table
select d.id, d.Name, d.Age, d.Group, 'DELETED'
from DELETED d;
end
I have a stored procedure that inserts a line in a table. This table has an auto incremented int primary key and a datetime2 column named CreationDate. I am calling it in a for loop via my C# code, and the loop is inside a transaction scope.
I run the program twice, first time with a for loop that turned 6 times and second time with a for loop that turned 2 times. When I executed this select on sql server I got a strange result
SELECT TOP 8
RequestId, CreationDate
FROM
PickupRequest
ORDER BY
CreationDate DESC
What I didn't get is the order of insertion: for example the line with Id=58001 has to be inserted after that with Id=58002 but this is not the case. Is that because I put my loop in a transaction scoope? or the precision in the datetime2 is not enough?
It is a question of speed and statement scope as well...
Try this:
--This will create a #numbers table with 1 mio numbers:
DECLARE #numbers TABLE(Nbr BIGINT);
WITH N(N) AS
(SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1)
,MoreN(N) AS
(SELECT 1 FROM N AS N1 CROSS JOIN N AS N2 CROSS JOIN N AS N3 CROSS JOIN N AS N4 CROSS JOIN N AS N5 CROSS JOIN N AS N6)
INSERT INTO #numbers(Nbr)
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM MoreN;
--This is a dummy table for inserts:
CREATE TABLE Dummy(ID INT IDENTITY,CreationDate DATETIME);
--Play around with the value for #Count. You can insert 1 mio rows in one go. Although this runs a while, all will have the same datetime value:
--Use a small number here and below, still the same time value
--Use a big count here and a small below will show a slightly later value for the second insert
DECLARE #Count INT = 1000;
INSERT INTO Dummy (CreationDate)
SELECT GETDATE()
FROM (SELECT TOP(#Count) 1 FROM #numbers) AS X(Y);
--A second insert
SET #Count = 10;
INSERT INTO Dummy (CreationDate)
SELECT GETDATE()
FROM (SELECT TOP(#Count) 1 FROM #numbers) AS X(Y);
SELECT * FROM Dummy;
--Clean up
GO
DROP TABLE Dummy;
You did your insertions pretty fast so the actual CreationDate values inserted in one program run had the same values. In case you're using datetime type, all the insertions may well occur in one millisecond. So ORDER BY CreationDate DESC by itself does not guarantee the select order to be that of insertion.
To get the desired order you need to sort by the RequestId as well:
SELECT TOP 8 RequestId, CreationDate
FROM PickupRequest
ORDER BY CreationDate DESC, RequestId DESC
getName_as_Rows is an array which contains some names.
I want to set an int value to 1 if record found in data base.
for(int i = 0; i<100; i++)
{
using (var command = new SqlCommand("select some column from some table where column = #Value", con1))
{
command.Parameters.AddWithValue("#Value", getName_as_Rows[i]);
con1.Open();
command.ExecuteNonQuery();
}
}
I am looking for:
bool recordexist;
if the above record exist then bool = 1 else 0 with in the loop.
If have to do some other stuff if the record exist.
To avoid making N queries to the database, something that could be very expensive in terms of processing, network and so worth, I suggest you to Join only once using a trick I learned. First you need a function in your database that splits a string into a table.
CREATE FUNCTION [DelimitedSplit8K]
--===== Define I/O parameters
(#pString VARCHAR(8000), #pDelimiter CHAR(1))
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 0 up to 10,000...
-- enough to cover VARCHAR(8000)
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "zero base" and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT 0 UNION ALL
SELECT TOP (DATALENGTH(ISNULL(#pString,1))) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT t.N+1
FROM cteTally t
WHERE (SUBSTRING(#pString,t.N,1) = #pDelimiter OR t.N = 0)
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY s.N1),
Item = SUBSTRING(#pString,s.N1,ISNULL(NULLIF(CHARINDEX(#pDelimiter,#pString,s.N1),0)-s.N1,8000))
FROM cteStart s
GO
Second, concatenate your 100 variables into 1 string:
"Value1", "Value 2", "Value 3"....
In Sql Server you can just join the values with your table
SELECT somecolumn FROM sometable t
INNER JOIN [DelimitedSplit8K](#DelimitedString, ',') v ON v.Item = t.somecolumn
So you find 100 strings at a time with only 1 query.
Use var result = command.ExecuteScalar() and check if result != null
But a better option than to loop would be to say use a select statement like
SELECT COUNT(*) FROM TABLE WHERE COLUMNVAL >= 0 AND COLUMNVAL < 100,
and run ExecuteScalar on that, and if the value is > 0, then set your variable to 1.
I have a table with the following structure
ID Name Parent
----------- -------------------------------------------------- -----------
1 Root NULL
2 Root_A 1
3 Root_B 1
4 Root_C 1
5 Root_C_A 4
6 Root_C_A_A 5
7 Root_C_A_A_A 6
So if I pass 7, I would like to get the following
Root --> Root_C --> Root_C_A --> Root_C_A_A
That means I want to traverse back to root from a child. How can I do it using SQL Server 2008 Stored Procedures or with other .Net techniques ?
I think I can accomplish the task using recursive function
You can implement recursion in Sql Server 2005+ using a common table expression. CTEs let you join to themselves in order to recurse. The CTE continues to recurse until no rows are returned, so you'll want to ensure you reliably meet that end condition.
declare #folders as table (id int, name nvarchar(20), parent int);
insert into #folders values(1, 'Root', null);
insert into #folders values(2, 'Root_A', 1);
insert into #folders values(3, 'Root_B', 1);
insert into #folders values(4, 'Root_C', 1);
insert into #folders values(5, 'Root_C_A', 4);
insert into #folders values(6, 'Root_C_A_A', 5);
insert into #folders values(7, 'Root_C_A_A_A', 6);
declare #folderID int;
set #folderID=7;
with Folders (id, name, parent, number) as
(
select ID, name, parent, 0 as number
from #folders
where id=#folderID
union all
select i.ID, i.Name, i.Parent, d.number + 1
from #folders as i
inner join Folders as d on d.Parent = i.ID
)
select id, name, number
from Folders
order by number desc;
try this:
declare #result varchar(100)
declare #id int
set #id = 7
select #result=Name,#id=Parent from temp where id=#id
while(exists(select 1 from temp where id=#id))
begin
select #id=Parent,#result=case #result when '' then Name else Name + '-->' end + #result from temp where id=#id
end
select #result