Refer to the calculated column within the column - sql

Column ORtg_home contains values differ from zeros and zeros. My purpose is to create a new column (X) in which there are only values without zeros. (After the first home game. If it would be a zero then give back the last value which is not zero from ORtg_home).
Team
Game_total
Home_away
Game_home
Game_away
ORtg_avg
ORtg_home
x
ATL
1
away
0
1
100
0
0
ATL
2
home
1
1
101
102
102
ATL
3
away
1
2
104
0
102
ATL
4
away
1
3
106
0
102
I tried the code below:
SELECT
[ORtg_home]
,(CASE WHEN [Home_away] = 'home' THEN ([ORtg_home])
ELSE
LAG(ORtg_home, 1) OVER (PARTITION BY Team ORDER BY [Game_total] ASC, Home_away ASC) END) as XXX
FROM [table1]
This gives 102 for the 3rd game, however the 4th game value in column x is still 0. Teams may vary. Currently table does not contain the date of the game, but it can be added.
CREATE TABLE [test1]
(
[Team] VARCHAR(3)
,[Game_total] INT
,[Home_away] VARCHAR(4)
,[Game_home] INT
,[Game_away] INT
,[ORtg_avg] FLOAT
,[ORtg_home] FLOAT
,[x] FLOAT
)
INSERT INTO [test1]
[Team], [Game_total], [Home_away], [Game_home], [Game_away, [ORtg_avg] ,[ORtg_home]
VALUES (ATL, 1, 'away', 0, 1, 100, 0)
VALUES (ATL, 2, 'home', 1, 1, 101, 102)
VALUES (ATL, 3, 'away', 1, 2, 104, 0)
VALUES (ATL, 4, 'away', 1, 3, 106, 0)

This would be easier if LAST_VALUE supported the IGNORE_NULLS clause as then you could just convert the zeroes to NULL and use that.
In the absence of this you can use the approach below (fiddle)
WITH T AS
(
SELECT *,
MAX(FORMAT(Game_total,'D10') + FORMAT(NULLIF(ORtg_home,0),'R')) OVER (PARTITION BY Team ORDER BY [Game_total] ASC) AS _x
FROM test1
)
SELECT Team,
Game_total,
Home_away,
Game_home,
Game_away,
ORtg_avg,
ORtg_home,
COALESCE(SUBSTRING(_x, 11, 100), 0E0) AS x
FROM T

Related

Choose a record from the table where two of the default values are 0 or 1, a bit tricky

Looking for a more elegant and most logical solution for:
The table:
index
id
by_default
text
1
1
0
AAA
2
1
1
ABA
3
1
0
ABC
4
2
0
BCA
5
2
0
BCB
The task is to find the minimum index value with defaults set to 1 and/or defaults set to 0.
I have the following code (not very elegant, but it works, also very slow):
declare #byd_1 as int=
(select min(t.index) idx from Table t where t.[id]=1 and t.by_default=1)
declare #byd_2 as int=
(select min(t.index) idx from Table t where t.[id]=1 and t.by_default=0)
select (case when #byd_1 is null then #byd_2 else #byd_1 end)
The tricky part is: sometimes the by_default column is always 0 (for example: id:2 may have no by_default values set) and as mentioned earlier the task is: need to get the minimum value of the index column.
What is the most elegant (one-line) code possible?
Using MSSQL
The expected results, according to the sample table, should be the following:
index
id
by_default
text
2
1
1
ABA
4
2
0
BCA
Edited to add text also.
A bit ugly but:
drop table #t
select *
into #t
from (
VALUES (1, 1, 0, N'AAA')
, (2, 1, 1, N'ABA')
, (3, 1, 0, N'ABC')
, (4, 2, 0, N'BCA')
, (5, 2, 0, N'BCB')
) t (index_,id,by_default,text)
select index_, id, by_default, text
from (
select min(index_) OVER(PARTITION BY id) AS minIndex
, MIN(case when by_default = 1 then index_ end) over(partition by id) AS minIndexDefault
, *
from #t
) t
where isnull(minIndexDefault, minIndex) = index_

Find row with the most true value columns

Table User
estado_id
user_id
shopping
mercado
feira
1
1
1
0
1
1
2
1
1
1
1
3
1
0
1
Table Cidade
estado_id
version_id
name
type
1
1
jose
shopping
1
2
maria
feira
1
2
maria
mercado
1
3
bruna
mercado
I need to compare the states, and bring only those that cover user x criteria's, but the user table may contain hundreds of users and I just have to return to which user x is bound. In the users table, I need to bring everyone that they think matches. If in the shopping and fair table the value is 1 and market to 0 then shopping and fair must be returned within these versions, I managed to bring up only the user data / id_state, however when I compare the user table = 1 with the city table type = shopping
But I cannot find a way to do that, because' it needs to be dynamic
WITH CTE AS (
SELECT cidade.*, uv.user_id, uv.shopping, uv.mercado, uv.feira
FROM cidade
INNER JOIN userst as uv ON cidade.estado_id = uv.estado_id
WHERE cidade.estado_id = 1 AND uv.user_id = 1
)
SELECT [type], shopping, mercado, feira
FROM CTE
My expectation
estado_id
version_id
name
type
user_id
1
1
jose
shopping
1
CREATE TABLE cidade(
estado_id int,
version_id int,
name varchar(255),
[type] varchar(255)
);
CREATE TABLE userst (
estado_id int,
user_id int,
shopping int,
mercado int,
feira int
);
INSERT INTO cidade (estado_id, version_id, name, [type])
VALUES (1, 1, 'jose', 'shopping')
, (1, 2, 'maria', 'feira')
, (1, 2, 'maria', 'mercado')
, (1, 3, 'bruna', 'mercado');
INSERT INTO userst (estado_id, user_id, shopping, mercado, feira)
VALUES (1, 1, 1, 0, 1)
, (1, 2, 1, 1, 1)
, (1, 3, 1, 0, 1);

Remove Rows That Sum Zero For A Given Key

I have a query that will result in a customer bill being created on our SSRS 2008 R2 server. The SQL Server instance is also 2008 R2. The query is large and I don't want to post the entire thing for security reasons, etc.
What I need to do with the example data below, is to remove the two rows with 73.19 and -73.19 from the result set. So, if two rows have the same absolute value in the LineBalance column and their sum is 0 AND if they have the same value in the REF1 column, the should be removed from the result set. The line with REF1 = 14598 and a line balance of 281.47 should still be returned in the result set and the other two rows below with REF1 = 14598 should not be returned.
The point of this is to "hide" accounting errors and their correction from the customer. by "hide" I mean, not show it on the bill they get in the mail. What happened here is the customer was mistakenly billed 73.19 when they should have been billed 281.47. So, our AR dept. returned 73.19 to their account and charged them the correct amount of 281.47. As you can see they all have the same REF1 value.
I would add a field that would contain explicit flag telling you that a certain charge was a mistake/reversal of a mistake and then it is trivial to filter out such rows. Doing it on the fly could make your reports rather slow.
But, to solve the given problem as is we can do like this. The solution assumes that SysInvNum is unique.
Create a table with sample data
DECLARE #T TABLE (SysInvNum int, REF1 int, LineBalance money);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (3344299, 14602, 558.83);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (3344298, 14598, 281.47);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (3344297, 14602, -95.98);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (3344296, 14598, -73.19);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (3341758, 14598, 73.19);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (11, 100, 50.00);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (12, 100, -50.00);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (13, 100, 50.00);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (21, 200, -50.00);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (22, 200, -50.00);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (23, 200, 50.00);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (31, 300, -50.00);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (32, 300, 50.00);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (33, 300, -50.00);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (34, 300, 50.00);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (41, 400, 50.00);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (42, 400, -50.00);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (43, 400, 50.00);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (44, 400, -50.00);
INSERT INTO #T (SysInvNum, REF1, LineBalance) VALUES (45, 400, 50.00);
I've added few more cases that have multiple mistakes.
Number and count rows
SELECT
SysInvNum
, REF1
, LineBalance
, ROW_NUMBER() OVER(PARTITION BY REF1, LineBalance ORDER BY SysInvNum) AS rn
, COUNT(*) OVER(PARTITION BY REF1, ABS(LineBalance)) AS cc1
FROM #T AS TT
This is the result set:
SysInvNum REF1 LineBalance rn cc1
11 100 50.00 1 3
12 100 -50.00 1 3
13 100 50.00 2 3
21 200 -50.00 1 3
23 200 50.00 1 3
22 200 -50.00 2 3
31 300 -50.00 1 4
32 300 50.00 1 4
33 300 -50.00 2 4
34 300 50.00 2 4
41 400 50.00 1 5
42 400 -50.00 1 5
43 400 50.00 2 5
44 400 -50.00 2 5
45 400 50.00 3 5
3341758 14598 73.19 1 2
3344296 14598 -73.19 1 2
3344298 14598 281.47 1 1
3344297 14602 -95.98 1 1
3344299 14602 558.83 1 1
You can see that those rows that have mistakes have count > 1. Also, pairs of mistakes have same row numbers. So, we need to remove/hide those rows that have count > 1 and those that have two same row numbers.
Determine rows to remove
WITH
CTE_rn
AS
(
SELECT
SysInvNum
, REF1
, LineBalance
, ROW_NUMBER() OVER(PARTITION BY REF1, LineBalance ORDER BY SysInvNum) AS rn
, COUNT(*) OVER(PARTITION BY REF1, ABS(LineBalance)) AS cc1
FROM #T AS TT
)
, CTE_ToRemove
AS
(
SELECT
SysInvNum
, REF1
, LineBalance
, COUNT(*) OVER(PARTITION BY REF1, rn) AS cc2
FROM CTE_rn
WHERE CTE_rn.cc1 > 1
)
SELECT *
FROM CTE_ToRemove
WHERE CTE_ToRemove.cc2 = 2
This is another intermediate result:
SysInvNum REF1 LineBalance cc2
12 100 -50.00 2
11 100 50.00 2
21 200 -50.00 2
23 200 50.00 2
32 300 50.00 2
31 300 -50.00 2
33 300 -50.00 2
34 300 50.00 2
42 400 -50.00 2
41 400 50.00 2
43 400 50.00 2
44 400 -50.00 2
3344296 14598 -73.19 2
3341758 14598 73.19 2
Now, we just put all this together.
Final query
WITH
CTE_rn
AS
(
SELECT
SysInvNum
, REF1
, LineBalance
, ROW_NUMBER() OVER(PARTITION BY REF1, LineBalance ORDER BY SysInvNum) AS rn
, COUNT(*) OVER(PARTITION BY REF1, ABS(LineBalance)) AS cc1
FROM #T AS TT
)
, CTE_ToRemove
AS
(
SELECT
SysInvNum
, REF1
, LineBalance
, COUNT(*) OVER(PARTITION BY REF1, rn) AS cc2
FROM CTE_rn
WHERE CTE_rn.cc1 > 1
)
SELECT *
FROM #T AS TT
WHERE
TT.SysInvNum NOT IN
(
SELECT CTE_ToRemove.SysInvNum
FROM CTE_ToRemove
WHERE CTE_ToRemove.cc2 = 2
)
ORDER BY SysInvNum;
Result:
SysInvNum REF1 LineBalance
13 100 50.00
22 200 -50.00
45 400 50.00
3344297 14602 -95.98
3344298 14598 281.47
3344299 14602 558.83
Note, that final result doesn't have any rows with REF = 300, because there were two corrected mistakes, that balanced each other completely.
Most AR/billing systems treat "credit memos" (the negative amount) similar to cash, in which case the -73.19 would get applied to the 73.19 LineBalance the same as if the customer had paid that amount, resulting in a $0 balance.
OPTION 1:
Do you handle cash receipts and applications in this sytem? If so, you may be able to pull data from those cash application tables to show the tie between SysInvNum 3344296 and 3341758.
OPTION 2:
I'm assuming that PayAdjust column is used to reduce the balance after a customer has paid, and that LineBalance is a calculated column that is Charges + PayAdjust.
Most of the time when this occurs, the AR department would be responsible for applying the credit memo to an open invoice, so that the PayAdjust column would net $0 between the 2 rows, and this would cause the LineBalance to also be $0 on each of the 2 rows. It may just be a training issue for the system that is being used.
This would cause the 3 rows in question to look like this, so you don't have an issue, you would just exclude the rows by adding where LineBalance <> 0 to your query since the AR department (which applied the credit to begin with and so knows the answer to this question) explicitly stated which LineBalance the credit applies to:
Option 2 Preferred data structure:
SysInvNum REF1 Charges PayAdjust LineBalance
----------- ----------- --------------------- --------------------- ---------------------
3344298 14598 281.47 0.00 281.47
3344296 14598 -73.19 73.19 0.00
3341758 14598 73.19 -73.19 0.00
OPTION 3:
Without having this data from Option 1 or 2, you have make many assumptions and run the risk of inadvertently hiding the wrong rows.
That being said, here is a query that attempts to do what you are asking, but I would highly recommend checking with the AR dept to see if they can update "PayAdjust" for these records instead.
I added several test cases of scenarios that could cause issues, but this may not cover all the bases.
This query will only hide rows where one distinct matching negative value is found for a positive value, for the same REF1 and same DueDate. It also makes sure that the original Charge invoice ID is prior to the credit since it can be assumed that a credit would not occur prior to the actual charge (Test case 6 shows both rows still because the credit has an SysInvNum that occurred before the charge). If more than once match is found per REF1, DueDate, and LineBalance, then it will not hide the corresponding charge and credit lines (test cases 2 & 4). Test case 3 sums in total to 0, but it still shows all 3 rows because the LineBalance values do not match exactly. These are all assumptions that I made to handle edge cases, so they can be adjusted as needed.
CREATE TABLE #SysInvTable (SysInvNum int not null primary key, REF1 int, Charges money, PayAdjust money, LineBalance as Charges + PayAdjust, DueDate date, REF2 int, Remark varchar(50), REM varchar(50));
INSERT INTO #SysInvTable(SysInvNum, REF1, Charges, PayAdjust, DueDate, Remark)
VALUES
--.....................................
--Your test case
(3344298, 14598, 281.47, 0, '2014-12-08','Your original test case. This one should stay.')
, (3344296, 14598, -73.19, 0, '2014-12-08',null)
, (3341758, 14598, 73.19, 0, '2014-12-08',null)
--.....................................
--Test case 2: How do you match these up?
, (2001, 2, 73.19, 0, '2015-01-06','Charge 2.1')
, (2002, 2, 73.19, 0, '2015-01-06','Charge 2.2')
, (2003, 2, 73.19, 0, '2015-01-06','Charge 2.3')
, (2004, 2, -73.19, 0, '2015-01-06','Credit for charge 2.3')
, (2005, 2, -73.19, 0, '2015-01-06','Credit for charge 2.1')
--.....................................
--Test case 3
, (3001, 3, 73.19, 0, '2015-01-06','Charge 3.1')
, (3002, 3, 73.19, 0, '2015-01-06','Charge 3.2')
, (3003, 3, -146.38, 0, '2015-01-06','Credit for charges 3.1 and 3.2')
--.....................................
--Test case 4: Do you hide 4001 or 4002?
, (4001, 4, 73.19, 0, '2015-01-06','Cable')
, (4002, 4, 73.19, 0, '2015-01-06','Internet')
, (4003, 4, -73.19, 0, '2015-01-06','Misc Credit')
--.....................................
--Test case 5: remove all lines except the first
, (5000, 5, 9.99, 0, '2015-01-06','Charge 5.0 (Should stay)')
, (5001, 5, 11.11, 0, '2015-01-06','Charge 5.1')
, (5002, 5, 22.22, 0, '2015-01-06','Charge 5.2')
, (5003, 5, 33.33, 0, '2015-01-06','Charge 5.3')
, (5004, 5, -11.11, 0, '2015-01-06','Credit for charge 5.1')
, (5005, 5, -33.33, 0, '2015-01-06','Credit for charge 5.3')
, (5006, 5, -22.22, 0, '2015-01-06','Credit for charge 5.2')
--.....................................
--Test case 6: credit occurs before charge, so keep both
, (6000, 6, -73.19, 0, '2015-01-06','Credit occurs before charge')
, (6001, 6, 73.19, 0, '2015-01-06','Charge 6.1')
;
SELECT i.*
FROM #SysInvTable i
WHERE i.SysInvNum not in
(
SELECT IngoreInvNum = case when c.N = 1 then max(t.SysInvNum) else min(t2.SysInvNum) end
FROM #SysInvTable t
INNER JOIN #SysInvTable t2
ON t.ref1 = t2.ref1
AND t.DueDate = t2.DueDate
CROSS APPLY (SELECT 1 AS N UNION ALL SELECT 2 as N) AS c --used to both both T and T2 SysInvNum's to exclude
WHERE 1=1
AND t.LineBalance > 0 AND t2.LineBalance < 0
AND t.SysInvNum < t2.SysInvNum --make sure the credit came in after the positive SysInvNum
AND t.LineBalance = t2.LineBalance * -1
GROUP BY t.REF1, t.DueDate, abs(t.LineBalance), c.n
HAVING Count(*) = 1
)
;
DROP TABLE #SysInvTable;
It's clunky, but this allows you to identify the matching negative offenders. You'll need to exclude the sysInvNum in both columns from your resultset
Create table #tmp( SysInvNum int,Ref1 int, LineBalance decimal(8,2))
insert into #tmp
values(3344299,14602,558.83)
,(3344298,14598,281.47)
,(3344297,14602,-95.98)
,(3344296,14598,-73.19)
,(3341758,14598,73.19)
--Select * From #tmp
Select *
INTO #t1
From #tmp
Where LineBalance < 0
--Select * From #t1
Select SysInvNum1 = #tmp.SysInvNum, SysInvNum2 = #T1.SysInvNum
INTO #T2
From #tmp
LEFT JOIN #t1 On #tmp.Ref1 = #T1.Ref1 and #tmp.LineBalance = -#T1.LineBalance
where #t1.SysInvNum is not null
Select * From
#tmp
Where SysInvNum not in(
Select SysInvNum1 from #t2
union Select SysInvNum2 from #t2
)
drop table #tmp
drop table #t1
drop table #t2
This solution works for SQL Server 2012 only, but decided to leave it here if someone has to do something similar in the future because it is pretty straight forward.
....
This should only remove two transactions for specific recepient if their sum is 0 and there are no other transactions between them (and the negative transaction happens after positive transaction). It is more conservative and safe.
There should be information about the order of transactions. Something like date column. Than you should order by this column instead of PRIMARY KEY column in PARTITION BY statement.
IF OBJECT_ID('Receipts', 'U') IS NOT NULL
DROP TABLE Receipts
CREATE TABLE Receipts
(
SysInvNum INT PRIMARY KEY IDENTITY(1,1),
REF1 INT,
LineBalance DECIMAL(10,2)
)
INSERT INTO Receipts
values
(14602,558.83),
(14598,281.47),
(14602,-95.98),
(14598,73.19),
(14598,-73.19),
(14598,73.19),
(14598,73.19),
(14598, 215.6),
(14598,73.19)
WITH ghosts AS
(
SELECT SysInvNum, REF1, LineBalance,
LAG(LineBalance, 1, 0) OVER (PARTITION BY REF1 ORDER BY SysInvNum) PreviousLineBalance,
LEAD(LineBalance, 1, 0) OVER (PARTITION BY REF1 ORDER BY SysInvNum) NextLineBalance
FROM Receipts
)
SELECT r.SysInvNum, r.REF1, r.LineBalance
FROM Receipts r
JOIN ghosts g On (r.SysInvNum = g.SysInvNum)
WHERE NOT (g.LineBalance + g.PreviousLineBalance = 0 AND g.LineBalance < 0)
AND NOT (g.LineBalance + g.NextLineBalance = 0 AND g.LineBalance > 0)
ORDER BY r.SysInvNum

SQL Update question

I am wondering how this can be achieved.
Let's say I have a table with two columns (IU(uniqueidentifier),(ID(int), SEL(char(1))
ID column has the following values in each row(ordered by IU):
0, 1, 2, 2, 0, 0, 1, 2, 2, 2, 0, 0, 4, 2, 2, 0, 0, 1, 2, 0, 0
I need to update column SEL with 'Y' for rows which are part of the group:
1, 2, 2, 2 ...
(Starts With 1 and in the next rows thare are 2's. (Group 4, 2, 2 is not correct).
So in this example column: SEL should be:
null, Y, Y, Y, null, null, Y, Y, Y, Y, null, null, 4, 2, 2, null, null, Y, Y, null, null
Thanks!
Here's a set-based approach.
DDL & sample data:
DECLARE #atable TABLE (
UI uniqueidentifier DEFAULT NEWSEQUENTIALID(),
ID int,
SEL char(1)
);
INSERT INTO #atable (ID)
SELECT 0 UNION ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 2 UNION ALL
SELECT 0 UNION ALL
SELECT 0 UNION ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 2 UNION ALL
SELECT 2 UNION ALL
SELECT 0 UNION ALL
SELECT 0 UNION ALL
SELECT 4 UNION ALL
SELECT 2 UNION ALL
SELECT 2 UNION ALL
SELECT 0 UNION ALL
SELECT 0 UNION ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 0 UNION ALL
SELECT 0;
The UPDATE statement:
WITH marked AS (
SELECT
*,
grp = CASE ID WHEN 0 THEN 0 ELSE 1 END
FROM #atable
),
grouped AS (
SELECT
*,
grpID = ROW_NUMBER() OVER (ORDER BY UI)
- ROW_NUMBER() OVER (PARTITION BY grp ORDER BY UI)
FROM marked
),
ranked AS (
SELECT
*,
rnk = ROW_NUMBER() OVER (PARTITION BY grp, grpID ORDER BY UI)
FROM grouped
)
UPDATE g
SET SEL = CASE r.ID
WHEN 0 THEN NULL
WHEN 1 THEN 'Y'
ELSE CAST(g.ID AS varchar)
END
FROM grouped g
INNER JOIN ranked r ON g.grp = r.grp AND g.grpID = r.grpID
WHERE r.rnk = 1;
The result of SELECT * FROM #atable after the update:
UI ID SEL
------------------------------------ ----------- ----
A4095E70-A0CC-E011-813B-20CF30905E89 0 NULL
A5095E70-A0CC-E011-813B-20CF30905E89 1 Y
A6095E70-A0CC-E011-813B-20CF30905E89 2 Y
A7095E70-A0CC-E011-813B-20CF30905E89 2 Y
A8095E70-A0CC-E011-813B-20CF30905E89 0 NULL
A9095E70-A0CC-E011-813B-20CF30905E89 0 NULL
AA095E70-A0CC-E011-813B-20CF30905E89 1 Y
AB095E70-A0CC-E011-813B-20CF30905E89 2 Y
AC095E70-A0CC-E011-813B-20CF30905E89 2 Y
AD095E70-A0CC-E011-813B-20CF30905E89 2 Y
AE095E70-A0CC-E011-813B-20CF30905E89 0 NULL
AF095E70-A0CC-E011-813B-20CF30905E89 0 NULL
B0095E70-A0CC-E011-813B-20CF30905E89 4 4
B1095E70-A0CC-E011-813B-20CF30905E89 2 2
B2095E70-A0CC-E011-813B-20CF30905E89 2 2
B3095E70-A0CC-E011-813B-20CF30905E89 0 NULL
B4095E70-A0CC-E011-813B-20CF30905E89 0 NULL
B5095E70-A0CC-E011-813B-20CF30905E89 1 Y
B6095E70-A0CC-E011-813B-20CF30905E89 2 Y
B7095E70-A0CC-E011-813B-20CF30905E89 0 NULL
B8095E70-A0CC-E011-813B-20CF30905E89 0 NULL
Rows in a table have no inherent order, so your grouping (1,2,2,2) is completely arbitrary. It is not guaranteed that your id's will always come in this order:
0, 1, 2, 2, 0, 0, 1, 2, 2, 2, 0, 0, 4, 2, 2, 0, 0, 1, 2, 0, 0
It could be that they come in a completely other order. So you need to specify a ORDER BY clause to get your order. As you have no other fields in your table but SEL and ID, I suppose this is not possible.
I really hope someone comes up with something better than this, because I hate this answer.
create table #test (
IU int identity primary key,
id int,
sel varchar(1)
)
insert into #test(id)
values (0), (1), (2), (2), (0), (0), (1), (2), (2), (2), (0), (0), (4), (2), (2), (0), (0), (1), (2), (0), (0)
DECLARE myCur CURSOR FORWARD_ONLY
FOR
select t.ID
from #test t
order by t.IU
FOR UPDATE OF t.sel
DECLARE #ID int, #lagSel varchar(1)
OPEN myCur
FETCH myCur INTO #ID
WHILE (##FETCH_STATUS = 0) BEGIN
SET #lagSel = CASE
WHEN #lagSel = 'Y' AND #ID in (1,2) THEN 'Y'
WHEN #ID = 1 THEN 'Y'
ELSE NULL
END
UPDATE #test
SET sel = #lagSel
WHERE CURRENT OF myCur
FETCH myCur INTO #ID
END
CLOSE myCur
DEALLOCATE myCur
A couple of things to note:
We're manually managing the value of #lagSel within the cursor so we can carry a value from one row to the next.
In order to be able to use the cursor FOR UPDATE, the table has to have a primary key.
In the UPDATE statement, the WHERE CURRENT OF myCur gives (at least in theory) a big performance gain over any other where clause.
I first tried doing this with lagged joins, but couldn't quite get it there. Here's my work in case someone else can do better:
select main.IU, main.id,
CASE
WHEN main.id = 1 THEN 'Y'
WHEN main.id = 2 AND lag.id in (1, 2) THEN 'Y'
ELSE NULL
END as new_sel
from #test main left outer join
#test lag on main.IU = lag.IU + 1
I think you have the design error here. MS SQL Server does not knows nothing about "next" and "previous" rows. if you try to select the records, the order of the records can be changed from time to time, unless you specify the ordering using ORDER BY statement.
I think you need to change the structure of the tables first.
EDIT: As I see, you have the field and can order your records. Now you can achive your goal using CURSOR.
Briefly, you can create the CURSOR FOR SELECT IU, ID ORDER BY IU ASC.
looping through the cursor records you can check the sequence of the values of ID field, and when sequence will be fully equivalent, you can update the corresponding record.

TSQL - Help with UNPIVOT

I am transforming data from this legacy table:
Phones(ID int, PhoneNumber, IsCell bit, IsDeskPhone bit, IsPager bit, IsFax bit)
These bit fields are not nullables and, potentially, all four bit fields can be 1.
How can I unpivot this thing so that I end up with a separate row for each bit field = 1. For instance, if the original table looks like this...
ID, PhoneNumber, IsCell, IsPager, IsDeskPhone, IsFax
----------------------------------------------------
1 123-4567 1 1 0 0
2 123-6567 0 0 1 0
3 123-7567 0 0 0 1
4 123-8567 0 0 1 0
... I want the result to be the following:
ID PhoneNumber Type
-----------------------
1 123-4567 Cell
1 123-4567 Pager
2 123-6567 Desk
3 123-7567 Fax
4 123-8567 Desk
Thanks!
2005/2008 version
SELECT ID, PhoneNumber, Type
FROM
(SELECT ID, PhoneNumber,IsCell, IsPager, IsDeskPhone, IsFax
FROM Phones) t
UNPIVOT
( quantity FOR Type IN
(IsCell, IsPager, IsDeskPhone, IsFax)
) AS u
where quantity = 1
see also Column To Row (UNPIVOT)
Try this:
DROP TABLE #Phones
CREATE TABLE #Phones
(
Id int,
PhoneNumber varchar(50),
IsCell bit,
IsPager bit,
IsDeskPhone bit,
IsFax bit
)
INSERT INTO #Phones VALUES (1, '123-4567', 1, 1, 0, 0)
INSERT INTO #Phones VALUES (2, '123-6567', 0, 0, 1, 0)
INSERT INTO #Phones VALUES (3, '123-7567', 0, 0, 0, 1)
INSERT INTO #Phones VALUES (4, '123-8567', 0, 0, 1, 0)
SELECT Id, PhoneNumber, [Type]
FROM (
SELECT Id, PhoneNumber,
Cell = IsCell, Pager = IsPager,
Desk = IsDeskPhone, Fax = IsFax
FROM #Phones
) a
UNPIVOT(
something FOR [Type] IN (Cell, Pager, Desk, Fax )
) as upvt