SQL field = other field minus another row - sql

Table has 2 cols: [nr] and [diff]
diff is empty (so far - need to fill)
nr has numbers:
1
2
45
677
43523452
on the diff column i need to add the differences between pairs
1 | 0
2 | 1
45 | 43
677 | 632
43523452 | 43522775
so basically something like :
update tbl set diff = #nr - #nrold where nr = #nr
but i don't want to use fetch next, because it's not cool, and it's slow (100.000) records
how can I do that with one update?

CREATE TABLE #T(nr INT,diff INT)
INSERT INTO #T (nr) SELECT 1
UNION SELECT 2
UNION SELECT 45
UNION SELECT 677
UNION SELECT 43523452
;WITH cte AS
(
SELECT nr,diff, ROW_NUMBER() OVER (ORDER BY nr) RN
FROM #T
)
UPDATE c1
SET diff = ISNULL(c1.nr - c2.nr,0)
FROM cte c1
LEFT OUTER JOIN cte c2 ON c2.RN+1= c1.RN
SELECT nr,diff FROM #T
DROP TABLE #T

Have a look at something like this (full example)
DECLARE #Table TABLE(
nr INT,
diff INT
)
INSERT INTO #Table (nr) SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 45 UNION ALL
SELECT 677 UNION ALL
SELECT 43523452
;WITH Vals AS (
SELECT nr,
diff,
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) xID
FROM #Table
)
UPDATE c
SET diff = c.nr - ISNULL(p.nr, 0)
FROM Vals c LEFT JOIN
Vals p ON c.xID = p.xID + 1
SELECT *
FROM #Table

try this -
update tablename
set diff = cast(nr as INT) - cast((select nr from tablename where diff is not null and nr = a.nr) as INT)
from tablename a
where diff is null
This is assuming you only have one older row for nr old in the table. else the subquery will return more than one value

Related

Case when duplicate add one more letter

For example: I have a table with these records below
1 A
2 A
3 B
4 C
...
and I need to migrate these record in to another table
1 AA
2 AB
3 B
4 C
...
Meaning if the record is duplicate, it will automatically add one more letter alphabetically.
Just a slightly different approach
Example
Declare #YourTable Table (ID int,[SomeCol] varchar(50))
Insert Into #YourTable Values
(1,'A')
,(2,'A')
,(3,'B')
,(4,'C')
Select *
,NewVal = concat(SomeCol,IIF(sum(1) over (partition by SomeCol)=1,'',char(64+row_number() over ( partition by SomeCol order by ID ))) )
From #YourTable
Returns
ID SomeCol NewVal
1 A AA
2 A AB
3 B B
4 C C
EDIT - Requested UPDATE
Declare #YourTable Table (ID int,[SomeCol] varchar(50))
Insert Into #YourTable Values
(1,'A')
,(2,'A')
,(3,'B')
,(4,'C')
Select *
,NewVal = concat(SomeCol,IIF(sum(1) over (partition by SomeCol)=1,'',replace(char(63+row_number() over ( partition by SomeCol order by ID )),'#','')) )
From #YourTable
Returns
ID SomeCol NewVal
1 A A
2 A AA
3 B B
4 C C
We might be able to handle this requirement with the help of a calendar table mapping secondary letters to duplicate sequence counts:
WITH letters AS (
SELECT 1 AS seq, 'A' AS let UNION ALL
SELECT 2, 'B' UNION ALL
SELECT 3, 'C' UNION ALL
...
SELECT 26, 'Z' UNION ALL
...
),
cte AS (
SELECT id, let, ROW_NUMBER() OVER (PARTITION BY let ORDER BY id) rn,
COUNT(*) OVER (PARTITION BY let) cnt
FROM yourTable
)
SELECT t1.id, t1.let + CASE WHEN t1.cnt > 1 THEN t2.let ELSE '' END AS let
FROM cte t1
LEFT JOIN letters t2
ON t1.id = t2.seq
ORDER BY t1.id;
Demo

Recursive cte to repeat several integers

I'd like a column of numbers:
Seven occurances of the integer 1, followed by 7 occurances of 2, followed by 7 occurances of 3 .... , followed by 7 occurances of n-1, followed by 7 occurances of n. Like so
Num
1
1
1
1
1
1
1
2
2
2
2
2
2
2
...
...
n-1
n-1
n-1
n-1
n-1
n-1
n-1
n
n
n
n
n
n
n
Unfortunately I've not progressed too far. My current attempt is the following, where n=4:
WITH
one AS
(
SELECT num = 1,
cnt = 0
UNION ALL
SELECT num = num,
cnt = cnt + 1
FROM one
WHERE cnt < 7
),
x AS
(
SELECT num,
cnt = 0
FROM one
UNION ALL
SELECT num = num + 1,
cnt = cnt + 1
FROM one
WHERE cnt < 4
)
SELECT *
FROM x
;WITH Numbers AS
(
SELECT n = 1
UNION ALL
SELECT n + 1
FROM Numbers
WHERE n+1 <= 10
),
se7en AS
(
SELECT n = 1
UNION ALL
SELECT n + 1
FROM se7en
WHERE n+1 <= 7
)
SELECT Numbers.n
FROM Numbers CROSS JOIN se7en
with x as
(select 1 as id
union all
select 2 as id
union all
select 3 as id
union all
select 4 as id
union all
select 5 as id
union all
select 6 as id
union all
select 7 as id)
select x1.* from x cross join x x1
The cross join will work in your case.
No need to use recursive CTE for this you can try set based approach solution try something like this. Kind of integer division.
If you have access to master database then use this.
;with cte as
(
SELECT top 1000 [7_seq] = ( ( Row_number()OVER(ORDER BY (SELECT NULL)) - 1 ) / 7 ) + 1
FROM sys.columns
)
select * from cte where [7_seq] <= #n
or use tally table to generate the numbers. I will prefer this solution
DECLARE #n INT = 10;
WITH Tally (num)
AS (
-- 1000 rows
SELECT Row_number()OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)),
seq
AS (SELECT [7_seq] = ( ( Row_number()
OVER(
ORDER BY (SELECT num)) - 1 ) / 7 ) + 1
FROM Tally)
SELECT [7_seq]
FROM seq
WHERE [7_seq] <= #n
WITH t1 AS (SELECT 0 as num UNION ALL SELECT 0)
,t2 AS (SELECT 0 as num FROM t1 as a CROSS JOIN t1 as b)
,t3 AS (SELECT 0 as num FROM t2 as a CROSS JOIN t2 as b)
,t4 AS (SELECT 0 as num FROM t3 as a CROSS JOIN t3 as b)
,Tally (number)
AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) FROM t4)
SELECT t1.number
FROM Tally as t1 cross join Tally as t2
where t2.number <=7
ORDER BY t1.number;
You can do It in following:
DECLARE #num INT = 1,
#sub INT = 0,
#max INT = 10,
#timesToRepeat INT = 7
CREATE TABLE #Temp (num INT)
WHILE (#num < #max + 1)
BEGIN
SET #sub = 0;
WHILE (#sub < #timesToRepeat)
BEGIN
INSERT INTO #Temp
SELECT #num x
SET #sub = #sub +1
END
SET #num = #num +1
END
SELECT * FROM #Temp
DROP TABLE #Temp
Set #max variable to number what you want to reach for now It is 10 so It will return result set like:
1
1
1
1
1
1
1
2
2
2
2
2
2
2
.
.
.
10
10
10
10
10
10
10
DECLARE #MAX INTEGER
SET #MAX = 5;
with cte as
(SELECT 7 as num
UNION ALL
SELECT num-1 as num from cte where num>1
),cte2 AS
(SELECT #MAX as num
UNION ALL
SELECT num-1 as num from cte2 where num>1)
select C2.num from cte C1,cte2 C2 ORDER by C2.num asc
Change the value of #MAX to reflect the value of n
Here is a slightly different way to do it.
select null num into #a
union all
select null
union all
select null
union all
select null
union all
select null
union all
select null
union all
select null
select * into #b from
(select rn = row_number()over (order by (select null)) from sys.objects A cross join sys.objects B) A
where rn <=10
select #b.rn as numbers from #a cross join #b
order by 1

How to add blank rows when select query sql

Example :
when I type :
select number, city from user
The results in the get is 3 rows. How to select the row that I can be automatically filled in 8 rows? What if using a loop ?
Desired Output:
if 3 rows then there are 5 empty rows,
if 4 rows then there are 4 empty rows, etc ..
While I don't understand the cause of this task, anyway you can do it like :
DECLARE #t TABLE ( ID INT )
DECLARE #c INT = 8
INSERT INTO #t
VALUES ( 1 ),
( 2 ),
( 3 );
WITH cte
AS ( SELECT 1 AS rn
UNION ALL
SELECT rn + 1
FROM cte
WHERE rn <= #c
)
SELECT TOP ( #c )
*
FROM ( SELECT ID
FROM #t
UNION ALL
SELECT NULL
FROM cte
) t
ORDER BY ID DESC
Output:
ID
3
2
1
NULL
NULL
NULL
NULL
NULL

How to comapre two columns of a table in sql?

In a table there are two columns:
-----------
| A | B |
-----------
| 1 | 5 |
| 2 | 1 |
| 3 | 2 |
| 4 | 1 |
-----------
Want a table where if A=B then
-------------------
|Match | notMatch|
-------------------
| 1 | 5 |
| 2 | 3 |
| Null | 4 |
-------------------
How can i do this?
I tried something which shows the Matched part
select distinct C.A as A from Table c inner join Table d on c.A=d.B
Try this:
;WITH TempTable(A, B) AS(
SELECT 1, 5 UNION ALL
SELECT 2, 1 UNION ALL
SELECT 3, 2 UNION ALL
SELECT 4, 1
)
,CTE(Val) AS(
SELECT A FROM TempTable UNION ALL
SELECT B FROM TempTable
)
,Match AS(
SELECT
Rn = ROW_NUMBER() OVER(ORDER BY Val),
Val
FROM CTE c
GROUP BY Val
HAVING COUNT(Val) > 1
)
,NotMatch AS(
SELECT
Rn = ROW_NUMBER() OVER(ORDER BY Val),
Val
FROM CTE c
GROUP BY Val
HAVING COUNT(Val) = 1
)
SELECT
Match = m.Val,
NotMatch= n.Val
FROM Match m
FULL JOIN NotMatch n
ON n.Rn = m.Rn
Try with EXCEPT, MINUS and INTERSECT Statements.
like this:
SELECT A FROM TABLE1 INTERSECT SELECT B FROM TABLE1;
You might want this:
SELECT DISTINCT
C.A as A
FROM
Table c
LEFT OUTER JOIN
Table d
ON
c.A=d.B
WHERE
d.ID IS NULL
Please Note that I use d.ID as an example because I don't see your schema. An alternate is to explicitly state all d.columns IS NULL in WHERE clause.
Your requirement is kind of - let's call it - interesting. Here is a way to solve it using pivot. Personally I would have chosen a different table structure and another way to select data:
Test data:
DECLARE #t table(A TINYINT, B TINYINT)
INSERT #t values
(1,5),(2,1),
(3,2),(4,1)
Query:
;WITH B AS
(
( SELECT A FROM #t
EXCEPT
SELECT B FROM #t)
UNION ALL
( SELECT B FROM #t
EXCEPT
SELECT A FROM #t)
), A AS
(
SELECT A val
FROM #t
INTERSECT
SELECT B
FROM #t
), combine as
(
SELECT val, 'A' col, row_number() over (order by (select 1)) rn FROM A
UNION ALL
SELECT A, 'B' col, row_number() over (order by (select 1)) rn
FROM B
)
SELECT [A], [B]
FROM combine
PIVOT (MAX(val) FOR [col] IN ([A], [B])) AS pvt
Result:
A B
1 3
2 4
NULL 5

Is it possible to write a sql query that is grouped based on a running total of a column?

It would be easier to explain with an example. Suppose I wanted to get at most 5 items per group.
My input would be a table looking like this:
Item Count
A 2
A 3
A 3
B 4
B 4
B 5
C 1
And my desired output would look like this:
Item Count
A 5
A>5 3
B 4
B>5 9
C 1
An alternative output that I could also work with would be
Item Count RunningTotal
A 2 2
A 3 5
A 3 8
B 4 4
B 4 8
B 5 13
C 1 1
I can use ROW_NUMBER() to get the top X records in each group, however my requirement is to get the top X items for each group, not X records. My mind is drawing a blank as to how to do this.
declare #yourTable table (item char(1), [count] int)
insert into #yourTable
select 'A', 2 union all
select 'A', 3 union all
select 'A', 3 union all
select 'B', 4 union all
select 'B', 4 union all
select 'B', 5 union all
select 'C', 1
;with cte(item, count, row) as (
select *, row_number() over ( partition by item order by item, [count])
from #yourTable
)
select t1.Item, t1.Count, sum(t2.count) as RunningTotal from cte t1
join cte t2 on t1.item = t2.item and t2.row <= t1.row
group by t1.item, t1.count, t1.row
Result:
Item Count RunningTotal
---- ----------- ------------
A 2 2
A 3 5
A 3 8
B 4 4
B 4 8
B 5 13
C 1 1
Considering the clarifications from your comment, you should be able to produce the second kid of output from your post by running this query:
select t.Item
, t.Count
, (select sum(tt.count)
from mytable tt
where t.item=tt.item and (tt.creating_user_priority < t.creating_user_priority or
( tt.creating_user_priority = t.creating_user_priority and tt.created_date < t.createdDate))
) as RunningTotal
from mytable t
declare #yourTable table (item char(1), [count] int)
insert into #yourTable
select 'A', 2 union all
select 'A', 3 union all
select 'A', 3 union all
select 'B', 4 union all
select 'B', 4 union all
select 'B', 5 union all
select 'C', 1
;with cte(item, count, row) as (
select *, row_number() over ( partition by item order by item, [count])
from #yourTable
)
select t1.row, t1.Item, t1.Count, sum(t2.count) as RunningTotal
into #RunTotal
from cte t1
join cte t2 on t1.item = t2.item and t2.row <= t1.row
group by t1.item, t1.count, t1.row
alter table #RunTotal
add GrandTotal int
update rt
set GrandTotal = gt.Total
from #RunTotal rt
left join (
select Item, sum(Count) Total
from #RunTotal rt
group by Item) gt
on rt.Item = gt.Item
select Item, max(RunningTotal)
from #RunTotal
where RunningTotal <= 5
group by Item
union
select a.Item + '>5', total - five
from (
select Item, max(GrandTotal) total
from #RunTotal
where GrandTotal > 5
group by Item
) a
left join (
select Item, max(RunningTotal) five
from #RunTotal
where RunningTotal <= 5
group by Item
) b
on a.Item = b.Item
I've updated the accepted answer and got your desired result.
SELECT Item, SUM(Count)
FROM mytable t
GROUP BY Item
HAVING SUM(Count) <=5
UNION
SELECT Item, 5
FROM mytable t
GROUP BY Item
HAVING SUM(Count) >5
UNION
SELECT t2.Item + '>5', Sum(t2.Count) - 5
FROM mytable t2
GOUP BY Item
HAVING SUM(Count) > 5
ORDER BY 1, 2
select 'A' as Name, 2 as Cnt
into #tmp
union all select 'A',3
union all select 'A',3
union all select 'B',4
union all select 'B',4
union all select 'B',5
union all select 'C',1
select Name, case when sum(cnt) > 5 then 5 else sum(cnt) end Cnt
from #tmp
group by Name
union
select Name+'>5', sum(cnt)-5 Cnt
from #tmp
group by Name
having sum(cnt) > 5
Here is what I have so far. I know it's not complete but... this should be a good starting point.
I can get your second output by using a temp table and an update pass:
DECLARE #Data TABLE
(
ID INT IDENTITY(1,1) PRIMARY KEY
,Value VARCHAR(5)
,Number INT
,Total INT
)
INSERT INTO #Data (Value, Number) VALUES ('A',2)
INSERT INTO #Data (Value, Number) VALUES ('A',3)
INSERT INTO #Data (Value, Number) VALUES ('A',3)
INSERT INTO #Data (Value, Number) VALUES ('B',4)
INSERT INTO #Data (Value, Number) VALUES ('B',4)
INSERT INTO #Data (Value, Number) VALUES ('B',5)
INSERT INTO #Data (Value, Number) VALUES ('C',1)
DECLARE
#Value VARCHAR(5)
,#Count INT
UPDATE #Data
SET
#Count = Total = CASE WHEN Value = #Value THEN Number + #Count ELSE Number END
,#Value = Value
FROM #Data AS D
SELECT
Value
,Number
,Total
FROM #Data
There may be better ways, but this should work.