Select all rows after sum of field reach specific number - sql

I have table like below
ID | Name | Value
---+------+-------
1 | X | 10
2 | y | 15
3 | z | 3
4 | A | 20
5 | B | 6
I want to select all rows that comes after sum(Value) reach specific number
Examples :-
If that specific number is 11, the result set will be:
2 | y | 15
3 | z | 3
4 | A | 20
5 | B | 6
If that specific number is 25, the result set will be
3 | z | 3
4 | A | 20
5 | B | 6
If that specific number is 30, the result set will be
4 | A | 20
5 | B | 6
If that specific number is 50, the result set will be
5 | B | 6

You could use this correlated subquery approach to calculate running totals:
declare #value int
set #value = 11
;WITH CTE AS
(
SELECT t.*,
SumBefore = (SELECT SUM(Value)
FROM dbo.TableName t2
WHERE t2.ID <= t.ID)
FROM TableName t
)
SELECT CTE.*
FROM CTE
WHERE SumBefore > #value
DEMO

You can use this simple query (If you have SQL Server 2008+):
SELECT ID, Name, Value
FROM (
SELECT *
, SUM(Value) OVER (ORDER BY ID) As SumNow
FROM yourTable) t
WHERE
(SumNow > ?);
[SQL Fiddle Demo]
For older version of SQL Servers you can use this query:
SELECT ID, Name, Value
FROM yourTable t
WHERE
((SELECT SUM(Value)
FROM yourTable ti
WHERE ti.ID <= t.ID) > ?);
[SQL Fiddle Demo]

Related

Adding values from previous row into current row

I have a simple table and I want add a value from previous row into current and value of current in next row so it keeps on going until the end.
Please have a look at the following table data.
ID Value ValueIncrement
1 2 0
2 3 5 (2+3)
3 9 14 (2+3+9)
4 6 20 (2+3+9+6)
5 3 23 (2+3+9+6+3)
6 1 24 (2+3+9+6+3+1)
7 2 26 (2+3+9+6+3+1+2)
8 0 26 (2+3+9+6+3+1+2+0)
9 2 30 (2+3+9+6+3+1+2+0+2)
I am looking for a select query which can enable me to add values continuously.
You can do a window sum:
select id, value, sum(value) over (order by id) valueincrement
from mytable
order by id
Demo on SQL Server 2014 Fiddle:
id | value | valueincrement
-: | ----: | -------------:
1 | 2 | 2
2 | 3 | 5
3 | 9 | 14
4 | 6 | 20
5 | 3 | 23
6 | 1 | 24
7 | 2 | 26
8 | 0 | 26
9 | 2 | 28
You could also use a correlated sub-query if you are working with a small table (otherwise,for performance reason, you should absolutely not use this approach if you can use window-functions). I am assuming your valueincrement column starting with a 0 is a typo and not by design. Let us know if otherwise.
select *, (select sum(value) from mytable t2 where t1.id >= t2.id) as valueincrement
from mytable t1
order by id;
You can try a recursive sum
INSERT INTO #tmpPrueba
VALUES(2),(3),(9),(6),(3),(1),(2),(0),(2)
CREATE TABLE #tmpTest
(nId INT IDENTITY(1,1),
nValor INT NOT NULL)
SELECT *
FROM #tmpTest;
SELECT SUM(nValor) OVER (ORDER BY nId ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
FROM #tmpTest;

Group by similar number SQL (oracle sql)

I would like to find the number of sales that have a similar purchase value from the following table:
sale_number | value
------------+-------
1 | 10
2 | 11
3 | 21
4 | 30
A vanilla group by statement groups by exact value:
select count(sale_number), value from table group by value
Would give:
count(sale_number) | value
------------+-------
1 | 10
1 | 11
1 | 21
1 | 30
Is it possible to group by inexact numbers with a threshold (say +/- 10%)? Giving the desired result:
count(sale_number) | value
------------+-------
2 | 10
2 | 11
1 | 21
1 | 30
You can do what you want with a correlated subquery:
select t.*,
(select count(*)
from t t2
where t2.value >= t.value * 0.9 and
t2.value <= t.value * 1.1
) as cnt
from t;

T-SQL: Best way to replace NULL with most recent non-null value?

Assume I have this table:
+----+-------+
| id | value |
+----+-------+
| 1 | 5 |
| 2 | 4 |
| 3 | 1 |
| 4 | NULL |
| 5 | NULL |
| 6 | 14 |
| 7 | NULL |
| 8 | 0 |
| 9 | 3 |
| 10 | NULL |
+----+-------+
I want to write a query that will replace any NULL value with the last value in the table that was not null in that column.
I want this result:
+----+-------+
| id | value |
+----+-------+
| 1 | 5 |
| 2 | 4 |
| 3 | 1 |
| 4 | 1 |
| 5 | 1 |
| 6 | 14 |
| 7 | 14 |
| 8 | 0 |
| 9 | 3 |
| 10 | 3 |
+----+-------+
If no previous value existed, then NULL is OK. Ideally, this should be able to work even with an ORDER BY. So for example, if I ORDER BY [id] DESC:
+----+-------+
| id | value |
+----+-------+
| 10 | NULL |
| 9 | 3 |
| 8 | 0 |
| 7 | 0 |
| 6 | 14 |
| 5 | 14 |
| 4 | 14 |
| 3 | 1 |
| 2 | 4 |
| 1 | 5 |
+----+-------+
Or even better if I ORDER BY [value] DESC:
+----+-------+
| id | value |
+----+-------+
| 6 | 14 |
| 1 | 5 |
| 2 | 4 |
| 9 | 3 |
| 3 | 1 |
| 8 | 0 |
| 4 | 0 |
| 5 | 0 |
| 7 | 0 |
| 10 | 0 |
+----+-------+
I think this might involve some kind of analytic function - somehow partitioning over the value column - but I'm not sure where to look.
You can use a running sum to set groups and use max to fill in the null values.
select id,max(value) over(partition by grp) as value
from (select id,value,sum(case when value is not null then 1 else 0 end) over(order by id) as grp
from tbl
) t
Change the over() clause to order by value desc to get the second result in the question.
The best way has been covered by Itzik Ben-Gan here:The Last non NULL Puzzle
Below is a solution which for 10 million rows and completes around in 20 seconds on my system
SELECT
id,
value1,
CAST(
SUBSTRING(
MAX(CAST(id AS binary(4)) + CAST(value1 AS binary(4)))
OVER (ORDER BY id
ROWS UNBOUNDED PRECEDING),
5, 4)
AS int) AS lastval
FROM dbo.T1;
This solution assumes your id column is indexed
You can also try using correlated subquery
select id,
case when value is not null then value else
(select top 1 value from table
where id < t.id and value is not null order by id desc) end value
from table t
Result :
id value
1 5
2 4
3 1
4 1
5 1
6 14
7 14
8 0
9 3
10 3
If the NULLs are scattered I use a WHILE loop to fill them in
However if the NULLs are in longer consecutive strings there are faster ways to do it.
So here's one approach:
First find a record that we want to update. It has NULL in this record and no NULL in the prior record
SELECT C.VALUE, N.ID
FROM TABLE C
INNER JOIN TABLE N
ON C.ID + 1 = N.ID
WHERE C.VALUE IS NOT NULL
AND N.VALUE IS NULL;
Use that to update: (bit hazy on this syntax but you get the idea)
UPDATE N
SET VALUE = C.Value
FROM TABLE C
INNER JOIN TABLE N
ON C.ID + 1 = N.ID
WHERE C.VALUE IS NOT NULL
AND N.VALUE IS NULL;
.. now just keep doing it till you run out of rows
-- This is needed to set ##ROWCOUNT to non zero
SELECT 1;
WHILE ##ROWCOUNT <> 0
BEGIN
UPDATE N
SET VALUE = C.Value
FROM TABLE C
INNER JOIN TABLE N
ON C.ID + 1 = N.ID
WHERE C.VALUE IS NOT NULL
AND N.VALUE IS NULL;
END
The other way is to use a similiar query to get a range of id's to update. This works much faster if your NULLS are usually against consecutive id's
Here is the one simple approach using OUTER APPLY
CREATE TABLE #table(id INT, value INT)
INSERT INTO #table VALUES
(1,5),
(2,4),
(3,1),
(4,NULL),
(5,NULL),
(6,14),
(7,NULL),
(8,0),
(9,3),
(10,NULL)
SELECT t.id, ISNULL(t.value, t3.value) value
FROM #table t
OUTER APPLY(SELECT id FROM #table WHERE id = t.id AND VALUE IS NULL) t2
OUTER APPLY(SELECT TOP 1 value
FROM #table WHERE id <= t2.id AND VALUE IS NOT NULL ORDER BY id DESC) t3
OUTPUT:
id VALUE
---------
1 5
2 4
3 1
4 1
5 1
6 14
7 14
8 0
9 3
10 3
Using this sample data:
if object_id('tempdb..#t1') is not null drop table #t1;
create table #t1 (id int primary key, [value] int null);
insert #t1 values(1,5),(2,4),(3,1),(4,NULL),(5,NULL),(6,14),(7,NULL),(8,0),(9,3),(10,NULL);
I came up with:
with x(id, [value], grouper) as (
select *, row_number() over (order by id)-sum(iif([value] is null,1,0)) over (order by id)
from #t1)
select id, min([value]) over (partition by grouper)
from x;
I noticed, however, that Vamsi Prabhala beat me to it... My solution is identical to what he posted. (arghhhh!). So I thought I'd try a recursive solution. Here's a pretty efficient use of a recursive cte (provided that ID is indexed):
with sorted as (select *, seqid = row_number() over (order by id) from #t1),
firstRecord as (select top(1) * from #t1 order by id),
prev as
(
select t.id, t.[value], lastid = 1, lastvalue = null
from sorted t
where t.id = 1
union all
select t2.id, t2.[value], lastid+1, isnull(prev.[value],lastvalue)
from sorted t2
join prev on t2.id = prev.lastid+1
)
select id, [value]=isnull([value],lastvalue)--, *
from prev;
Normally I don't like recursive cte's (rCte for short) but in this case it offered an elegant solution and was faster than using the window aggregate function (sum over, min over...). Note the execution plans, the rcte on the bottom. The rCTE get's it done with two index seeks, one of which is for just one row. Unlike the window aggregate solution, the rcte does not require a sort. Running this with statistics io on; the rcte produces much less IO.
All this said, don't use either of these solutions, What the TheGameiswar posted will perform the best by far. His solution on a properly indexed id column would be lightening fast.
Following UPDATE statement can be used, please test it before use
update #table
set value = newvalue
from (
select
s.id, s.value,
(select top 1 t.value from #table t where t.id <= s.id and t.value is not null order by t.id desc) as newvalue
from #table S
) u
where #table.id = u.id and #table.value is null
stop worrying..here's the answer for you :)
SELECT *
INTO #TempIsNOtNull
FROM YourTable
WHERE value IS NOT NULL
SELECT *
INTO #TempIsNull
FROM YourTable
WHERE value IS NULL
UPDATE YourTable
SEt YourTable.value = UpdateDtls.value
FROM YourTable
JOIN (
SELECT OuterTab1.id,
#TempIsNOtNull.value
FROM #TempIsNull OuterTab1
CROSS JOIN #TempIsNOtNull
WHERE OuterTab1.id - #TempIsNOtNull.id > 0
AND (OuterTab1.id - #TempIsNOtNull.id) = ( SELECT TOP 1
OuterTab1.id - #TempIsNOtNull.id
FROM #TempIsNull InnerTab
CROSS JOIN #TempIsNOtNull
WHERE OuterTab1.id - #TempIsNOtNull.id > 0
AND OuterTab1.id = InnerTab.id
ORDER BY (OuterTab1.id - #TempIsNOtNull.id) ASC) ) AS UpdateDtls
ON (YourTable.id = UpdateDtls.id)

RANK, ROW_NUMBER on T-SQL

I have rows like this in SQL Server 2014:
id | fld1
---+-----
1 | 100
2 | 100
3 | 80
4 | 102
5 | 100
6 | 80
7 | 102
I would need a partition that without changing order would return:
NewFld | id | fld1
-------+----+------
1 | 1 | 100
1 | 2 | 100
2 | 3 | 80
3 | 4 | 102
1 | 5 | 100
2 | 6 | 80
3 | 7 | 102
Newfld should return the same value based on fld1 without changing ordering given by id.
I tried with ROW_NUMBER, RANK, DENSE_RANK but nothing works for me.
View this fiddle
Use min() over() in a subquery to establish the ordering values needed for the dense_rank().
SELECT id
, Fld1
, DENSE_RANK() OVER (order by fld1_idmin) AS Rank
FROM (
SELECT id
, fld1
, Min (id) over (partition by fld1) fld1_idmin
FROM yourtable
) d
ORDER BY ID
With an index on FLD1 using these window functions needs just a single index scan for this query. See this SQLfiddle
you may use this
with mytab as
(
SELECT *
,(SELECT MIN(ID) FROM yourtable sub where sub.fld1 = yourtable.fld1) as ranks
FROM yourtable
)
SELECT ID ,fld1 , DENSE_RANK()OVER(ORDER BY Ranks)
FROM mytab
ORDER BY ID
view this fiddle

Select non-unique id where no row meets criteria

Say I have this table, and I want to select the IDs where all D is < 4. In this case it would only select ID 1 because 2's D>4, and 3 has a D>4
+----+---+------+
| ID | D | U-ID |
+----+---+------+
| 1 | 1 | a |
+----+---+------+
| 1 | 2 | b |
+----+---+------+
| 2 | 5 | c |
+----+---+------+
| 3 | 5 | d |
+----+---+------+
| 3 | 2 | e |
+----+---+------+
| 3 | 3 | f |
+----+---+------+
I really don't even know where to start making a query for this, and my sql isn't good enough yet to know what to google, so I'm sorry if this has been asked before.
I would simply do:
select id
from table
group by id
having max(d) < 4;
If you happened to want all the original rows, I would use a window function:
select t.*
from (select t.*, max(d) over (partition by id) as maxd
from t
) t
where maxd < 4;
Here's one option using conditional aggregation:
select id
from yourtable
group by id
having count(case when d >= 4 then 1 end) = 0
SQL Fiddle Demo
If you need all the data from the corresponding rows/columns, you can either join back to the table using the above, or alternatively you could use not exists:
select *
from yourtable t
where not exists (
select 1
from yourtable t2
where t.id = t2.id and
t2.d >= 4
)
use this query.
select ID from yourtablename where D < 4;