How to increment grouping number in query if consecutive values don't satisfy conditions defined? - sql

I will describe problem briefly.
----------------------------------------------------------------------------------------------------
| Total UnitName UnitValue PartlyStatus PartlyValue CountMetric CountValue | RowNo
| |
| 79 A 7654 B 0 C 360 | 1
| 79 A 7656 B 0 C 360 | 2
| 79 A 7657 B 0 C 360 | 2
| 79 A 7658 B 0 C 360 | 2
| 79 A 7659 B 1 C 240 | 3
| 79 A 7660 B 0 C 360 | 4
| 79 A 7662 B 1 C 240 | 5
| 79 A 7663 B 1 C 240 | 5
| 79 A 7664 B 1 C 240 | 5
| 79 A 7665 B 1 C 240 | 5
| 79 A 7667 B 1 C 240 | 6
| 79 A 7668 B 1 C 240 | 6
| 79 A 7669 B 1 C 240 | 6
| 79 A 7670 B 0 C 360 | 7
| 79 A 7671 B 0 C 360 | 7
| 79 A 7672 B 0 C 360 | 7
---------------------------------------------------------------------------------------------------
I have to create new row in my table in SQL Server Reporting Services(SSRS) if constraint is not satisfied.
Rules that i have to apply:
If UnitValue Numbers are not consecutive, use next row.
If binary values of partlyValue changes, use next row.
I have to write a query that creates a RowNo, which increments if conditions are not satisfied.
The table that i show is a derived result from long query to demonstrate problem. RowNo column is written for showing intended result.
My question is asked for understanding and thinking about elegant approaches to solve problem,
so conceptual query examples or solutions are fine for me as long as it puts me in a right direction.

I think you just want window functions. It is a little hard to follow the logic but this does what you want:
select t.*,
sum(case when prev_uv = unitvalue - 1 and
prev_pv = partlyvalue
then 0 -- no new group
else 1
end) over (order by unitvalue) as rowno
from (select t.*,
lag(unitvalue) over (order by unitvalue) as prev_uv,
lag(partlyvalue) over (order by unitvalue) as prev_pv
from t
) t;

You need to write functions in your solution explorer.

Related

Distribute sequential SQL results evenly based on count

I have SQL results that I need to break into item ranges and the count distributed evenly across a number of tasks. What is a good way to do this?
My data looks like this.
+------+-------+----------+
| Item | Count | ItmGroup |
+------+-------+----------+
| 1A | 100 | 1 |
| 1B | 25 | 1 |
| 1C | 2 | 1 |
| 1D | 6 | 1 |
| 2A | 88 | 2 |
| 2B | 10 | 2 |
| 2C | 122 | 2 |
| 2D | 12 | 2 |
| 3A | 4 | 3 |
| 3B | 103 | 3 |
| 3C | 1 | 3 |
| 3D | 22 | 3 |
| 4A | 55 | 4 |
| 4B | 42 | 4 |
| 4C | 100 | 4 |
| 4D | 1 | 4 |
+------+-------+----------+
Item = the item code.
Count = this context it is determining the popularity of the item. This can be used to RANK items if need be.
ItmGroup - this is a parent value for the Itm column. Item is contained in a Group.
What differentiates this from other similar questions I'veviewed is that the ranges I need to determine cannot be taken out of the order they show in this table. We can do Item Range from A1 to B3, in other words, they can cross over ItmGroups, but they must remain in alphanumeric order by Item.
The expected result would be item ranges that evenly distribute the total count.
+------+-------+----------+
| FrItem | ToItem | TotCount|
+------+-------+----------+
| 1A | 2D | 134 |
| 3A | 3D | 130 |
(etc)
Provided you've happy with a rough estimate, this will split the data in to two groups.
The first group will always have as many records as possible, but no more than half of the total count (and group 2 will have the rest).
WITH
cumulative AS
(
SELECT
*,
SUM([Count]) OVER (ORDER BY Item) AS cumulativeCount,
SUM([Count]) OVER () AS totalCount
FROM
yourData
)
SELECT
MIN(item) AS frItem,
MAX(item) AS toItem,
SUM([Count]) AS TotCount
FROM
cumulative
GROUP BY
CASE WHEN cumulativeCount <= totalCount / 2 THEN 0 ELSE 1 END
ORDER BY
CASE WHEN cumulativeCount <= totalCount / 2 THEN 0 ELSE 1 END
To split the data in to 5 portions, it's similar...
GROUP BY
CASE WHEN cumulativeCount <= totalCount * 1/5 THEN 0
WHEN cumulativeCount <= totalCount * 2/5 THEN 1
WHEN cumulativeCount <= totalCount * 3/5 THEN 2
WHEN cumulativeCount <= totalCount * 4/5 THEN 3
ELSE 4 END
Depending on your data this isn't necessarily ideal
Item | Count GroupAsDefinedAbove IdealGroup
------+-------
1A | 4 1 1
2A | 5 2 1
3A | 8 2 2
If you want something that can get the two groups as close in size as possible, that's a lot more complex.
Same as the accepted answer, except declaring a batch number and an addition to the select statement in the WITH cumulativeCte to prevent a remainder.
DECLARE #BatchCount NUMERIC(4,2) = 5.00;
WITH
cumulativeCte AS
(
SELECT
*,
SUM(r.[Count]) OVER (ORDER BY Item) AS cumulativeCount,
SUM(r.[Count]) OVER () AS totalCount
,CEILING(SUM(r.[Count]) OVER (ORDER BY IM.MMITNO ASC) / (SUM(r.[Count]) OVER () / #BatchCount)) AS BatchNo
FROM
records r
)
SELECT
MIN(c.Item) AS frItem,
MAX(c.Item) AS toItem,
SUM(c.[Count]) AS TotCount,
c.BatchNo
FROM
cumulativeCte c
GROUP BY
c.BatchNo
ORDER BY
c.BatchNo

SQL Server sum field from previous calculation

In SQL Server, I have table with 4 column
artid num A B
46 1 417636000 0
47 1 15024000 0
102 1 3418105650 0
226 1 1160601286 0
60 668 260000 0
69 668 5500000 0
I want in result set create new column for some calculation
This column should have value like this:
artid num a b newColumnValue
----------- ----------- ---------------------- ---------------------- ----------------------
46 1 417636000 0 a-b+previous newColumnValue
I write this query, but I can't get previous newColumnValue:
select *, (a- b+ lag(a- b, 1, a- b) over (order by num,artid)) as newColumnValue
FROM MainTbl
ORDER BY num,artid
i get this result
artid num a b newColumnValue
----------- ----------- ---------------------- ---------------------- ----------------------
46 1 417636000 0 417636000
47 1 15024000 0 432660000
102 1 3418105650 0 3433129650
226 1 1160601286 0 4578706936
60 668 260000 0 1160861286
69 668 5500000 0 5760000
i want get this result
artid num a b newColumnValue
----------- ----------- ---------------------- ---------------------- ----------------------
46 1 417636000 0 417636000
47 1 15024000 0 432660000
102 1 3418105650 0 3850765650
226 1 1160601286 0 5011366936
60 668 260000 0 5011626936
69 668 5500000 0 5017126936
You want cumulative sums (well, the difference between them):
select a, b, sum(a - b) over (order by num, artid)
from mytbl;
Note: SQL tables represent unordered sets. You need a column to specify the ordering to define previous. If you really only have two columns, then I might assume the ordering is based on a, and the query would be:
select a, b, sum(a - b) over (order by a)
from mytbl;
Given the following example data,
+----+---+---+
| Id | A | B |
+----+---+---+
| 1 | 2 | 3 |
+----+---+---+
| 2 | 3 | 4 |
+----+---+---+
| 3 | 4 | 5 |
+----+---+---+
| 4 | 5 | 6 |
+----+---+---+
| 5 | 6 | 7 |
+----+---+---+
the following short SQL statement produces the desired output:
select A - B + lag(A - B, 1, 0) over (order by id)
from test
+----+
| -1 |
+----+
| -2 |
+----+
| -2 |
+----+
| -2 |
+----+
| -2 |
+----+
Note that the Lag function takes three arguments: the first is the expression you would like evaluated for the "lagged" record, the second is the amount of the lag (defaults to 1), and the third is the value to return if the expression cannot be computed (e.g. if it is the first record).

Pair entry of every nth row with entry of every (n+1)th row

I have a result table
id | name | wins
----+-------------------
57 | Paul | 10
64 | Sven | 9
62 | Peter | 9
59 | Marina | 8
58 | Carlos | 4
60 | Pamela | 3
61 | Marcus | 2
63 | Hank | 1
Where I want to pair every nth entry with every (n+1)th entry, such that the resulting table looks like that:
id | name | id | name
----+-------------------
57 | Paul | 64 | Sven
62 | Peter | 59 | Marina
58 | Carlos | 60 | Pamela
61 | Marcus | 63 | Hank
Which SQL statement would achieve that?
;WITH cte AS (
SELECT *,ROW_NUMBER() OVER (ORDER BY Wins DESC) as RowNum
FROM
#Table
)
SELECT *
FROM
cte c1
LEFT JOIN cte c2
ON c1.RowNum + 1 = c2.RowNum
WHERE
c1.RowNum % 2 <> 0
Generate a ROW_NUMBER to use, seeing you have a third Column replace (SELECT NULL) in the Order by statement with that third column.
Then select all rows that are Odd Row numbers (remainder of RowNum divided by 2 <> 0 ) and self join back to itself with RowNum + 1. If you have an odd number of Rows you might consider using LEFT JOIN so you don't drop off the 1 row that won't have a match.

How to use previous row's column's value for calculating the next row's column's value

I have a table
Id | Aisle | OddEven | Bay | Size | Y-Axis
3 | A1 | Even | 14 | 10 | 100
1 | A1 | Even | 16 | 10 |
6 | A1 | Even | 20 | 10 |
12 | A1 | Even | 26 | 5 | 150
10 | A1 | Even | 28 | 5 |
11 | A1 | Even | 32 | 5 |
2 | A1 | Odd | 13 | 10 | 100
5 | A1 | Odd | 17 | 10 |
4 | A1 | Odd | 19 | 10 |
9 | A1 | Odd | 23 | 5 | 150
7 | A1 | Odd | 25 | 5 |
8 | A1 | Odd | 29 | 5 |
want to look like this
Id | Aisle | OddEven | Bay | Size | Y-Axis
1 | A1 | Even | 14 | 10 | 100
2 | A1 | Even | 16 | 10 | 110
3 | A1 | Even | 20 | 10 | 120
4 | A1 | Even | 26 | 5 | 150
5 | A1 | Even | 28 | 5 | 155
6 | A1 | Even | 32 | 5 | 160
7 | A1 | Odd | 13 | 10 | 100
8 | A1 | Odd | 17 | 10 | 110
9 | A1 | Odd | 19 | 10 | 120
10 | A1 | Odd | 23 | 5 | 150
11 | A1 | Odd | 25 | 5 | 155
12 | A1 | Odd | 29 | 5 | 160
I need a select query and update query. What its doing is there are already some Y-Axis Number been filled (at the start of the Odd/Even) then I need to take the previous row's Y-Axis column's value and adds to the current rows's size which = to current Y-Axis. Needs to keep doing it until it finds another Y-Axis has the value it skips the calculation and next row is using that number.
My thinking process is this:
Id will definitely be used, however, the Id is not sequence as shown my example
so I need to have
ROW_Number OVER (PARTITION BY Aisle,OddEven,Bay Order BY Aisle,OddEven,Bay)
Then some kind of JOIN the same table but the ON is T1.RN = T2.RN - 1
Where I am stuck is but the first row has not previous value it will try to update that value.
Anyone have an idea for SQL Query 2008 for Select and Update will be greatly appreciated! Thanks.
You seem to want a cumulative sum. This would be easier in SQL Server 2012+. You can do this in SQL Server 2008 using outer apply:
select t.*, cume_value
from t outer apply
(select sum(size) + sum(yaxis) as cume_value
from t t2
where t2.aisle = t.aisle and t2.oddeven = t.oddeven and
t2.bay < t.bay
) t2;
A little more difficult on 2008, but I think this is what you are looking for
Declare #Table table (Id int,Aisle varchar(25),OddEven varchar(25),Bay int,Size int,[Y-Axis] int)
Insert Into #Table values
(3,'A1','Even',14,10 ,100),
(1,'A1','Even',16,10 ,0),
(6,'A1','Even',20,10 ,0),
(12,'A1','Even',26,5,150),
(10,'A1','Even',28,5,0),
(11,'A1','Even',32,5,0),
(2,'A1','Odd',13,10 ,100),
(5,'A1','Odd',17,10 ,0),
(4,'A1','Odd',19,10 ,0),
(9,'A1','Odd',23,5,150),
(7,'A1','Odd',25,5,0),
(8,'A1','Odd',29,5,0)
;with cteBase as (
Select *
,IDNew=Row_Number() over (Order By Aisle,Bay)
,RowNr=Row_Number() over (Order By Aisle,OddEven,Bay)
From #Table
)
, cteGroup as (Select TmpRowNr=RowNr,GrpNr=Row_Number() over (Order By RowNr) from cteBase where [Y-Axis]>0)
, cteFinal as (
Select A.*
,GrpNr = (Select max(GrpNr) from cteGroup Where TmpRowNr<=RowNr)
From cteBase A
)
Select ID=Row_Number() over (Order By A.OddEven,A.Bay)
,A.Aisle
,A.OddEven
,A.Bay
,A.Size
,[Y-Axis] = Sum(case when B.[Y-Axis]>0 then B.[Y-Axis] else B.Size end)
From cteFinal A
Join cteFinal B on (B.RowNr<=A.RowNr and A.GrpNr=B.GrpNr)
Group By
A.IDNew
,A.Aisle
,A.OddEven
,A.Bay
,A.Size
Order By A.OddEven,A.Bay
Returns
ID Aisle OddEven Bay Size Y-Axis
1 A1 Even 14 10 100
2 A1 Even 16 10 110
3 A1 Even 20 10 120
4 A1 Even 26 5 150
5 A1 Even 28 5 155
6 A1 Even 32 5 160
7 A1 Odd 13 10 100
8 A1 Odd 17 10 110
9 A1 Odd 19 10 120
10 A1 Odd 23 5 150
11 A1 Odd 25 5 155
12 A1 Odd 29 5 160
I gotta leave my computer so update query should be easy to move on from here.
Below is the select query;
select row_number() over (order by oddeven,bay) id,
Aisle,
OddEven,
Bay,
Size,
max(ISNULL([Y-Axis],0)) over (partition by Aisle, OddEven,Size order by bay)
+ sum(CASE WHEN [Y-Axis] is null THEN Size ELSE 0 END) over (partition by Aisle,OddEven,size order by Bay) as [Y-Axis]
from oddseven
order by id

How to write the SQL query?

Suppose I have the following table, How can I write the SQL query to find a set of results which are the minimum c for each b? for example, I want (7, 45) and (16, 69) for the following table.
a | b | c
-----+--------+----
3 | 7 | 53
2 | 7 | 49
1 | 7 | 45
5 | 7 | 61
13 | 16 | 69
15 | 16 | 77
6 | 7 | 65
4 | 7 | 57
14 | 16 | 73
Just use GROUP BY:
SELECT b, MIN(c)
FROM yourTable
GROUP BY b
You can do this using distinct on in Postgres:
select distinct on (b) b, c
from t
order by b, c asc;
An alternative is group by:
select b, min(c)
from t
group by b;
You can try both and see which is faster.