SQL Fiddle
I have a table of values that some need attention:
| ID | AddedDate |
|---------|-------------|
| 1 | 2010-04-01 |
| 2 | 2010-04-01 |
| 3 | 2010-04-02 |
| 4 | 2010-04-02 |
| 5 | NULL | <----------- needs attention
| 6 | 2010-04-02 |
| 7 | 2010-04-03 |
| 8 | 2010-04-04 |
| 9 | 2010-04-04 |
| 2432659 | 2016-06-15 |
| 2432650 | 2016-06-16 |
| 2432651 | 2016-06-17 |
| 2432672 | 2016-06-18 |
| 2432673 | NULL | <----------- needs attention
| 2432674 | 2016-06-20 |
| 2432685 | 2016-06-21 |
I want to select the rows where AddedDate
is null, and i want to select rows around it. In this example question it would be sufficient to say rows where the ID
is ±3. This means i want:
| ID | AddedDate |
|---------|-------------|
| 2 | 2010-04-01 | ─╮
| 3 | 2010-04-02 | │
| 4 | 2010-04-02 | │
| 5 | NULL | ├──ID values ±3
| 6 | 2010-04-02 | │
| 7 | 2010-04-03 | │
| 8 | 2010-04-04 | ─╯
| 2432672 | 2016-06-18 | ─╮
| 2432673 | NULL | ├──ID values ±3
| 2432674 | 2016-06-20 | ─╯
Note: In reality it's a table of 9M rows, and 15k need attention.
First i create a query that builds the ranges i'm interested in returning:
SELECT
ID-3 AS [Low ID],
ID+3 AS [High ID]
FROM Items
WHERE AddedDate IS NULL
Low ID High ID
------- -------
2 8
2432670 2432676
So my initial attempt to use this does work:
WITH dt AS (
SELECT ID-3 AS Low, ID+3 AS High
FROM Items
WHERE AddedDate IS NULL
)
SELECT * FROM Items
WHERE EXISTS(
SELECT 1 FROM dt
WHERE Items.ID BETWEEN dt.Low AND dt.High)
But when i try it on real data:
There's probably a more efficient way.
To select rows using selection symbols for character or graphic data, use the LIKE keyword in a WHERE clause, and the underscore and percent sign as selection symbols. You can create multiple row conditions, and use the AND, OR, or IN keywords to connect the conditions.
You can concatenate rows into single string using COALESCE method. This COALESCE method can be used in SQL Server version 2008 and higher. All you have to do is, declare a varchar variable and inside the coalesce, concat the variable with comma and the column, then assign the COALESCE to the variable.
Now assume after selecting row number 4, we need to select all the rows which are below the selected row, then we can press another shortcut key “Shift + Ctrl + Down Arrow.” So the moment you press this, it will select all the rows which are below the selected row.
Select the row header of the first row in your selected range. Press down the SHIFT key on your keyboard (if you’re on a Mac, then press down on the CMD key). While the SHIFT key is pressed, select the last row of the range that you want to select.
Select the row header of the first row that you want to select. Press down the CTRL key of your keyboard. While the CTRL key is pressed, select row headers of subsequent rows that you want to select one by one.
To get the every 3rd (nth) row, we change the number to divide by to 3 (n). We can switch the filter on to filter on the MOD result required to show specific rows. To get the value from every other row or nth row, we can use the OFFSET and ROW functions. We will walkthrough this below.
This is your existing logic rewritten using an moving max:
WITH dt AS (
SELECT
ID, AddedDate,
-- check if there's a NULL within a range of +/- 3 rows
-- and remember it's ID
max(case when AddedDate is null then id end)
over (order by id
rows between 3 preceding and 3 following) as NullID
FROM Items
)
SELECT *
FROM dt
where id between NullID-3 and NullID+3
Here is one method that uses the windowing clause:
select i.*
from (select i.*,
count(*) over (order by id rows between 3 preceding and 1 preceding) as cnt_prec,
count(*) over (order by id rows between 1 following and 3 following) as cnt_foll,
count(addeddate) over (order by id rows between 3 preceding and 1 preceding) as cnt_ad_prec,
count(addeddate) over (order by id rows between 1 following and 3 following) as cnt_ad_foll
from items
) i
where cnt_ad_prec <> cnt_prec or
cnt_ad_foll <> cnt_foll or
addeddate is null;
order by id;
This returns all rows that have NULL
in the column or are within three rows of a NULL
.
The need for the comparison to the count is to avoid the edge issues on the smallest and largest ids.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With