LEAD/LAG SQL Server 2012/Gaps and Islands - sql-server-2012

I'm having a few issues with LEAD/LAG. For each row within a set of IDs I'm wanting to get the previous/next source where isAQI = 1. Desired output is as follows in prevAQI and nextAQI columns.
I've tried the same approach as Lag() with conditon in sql server, but with no luck. Any help would be much appreciated!
Sample data as follows:
DECLARE #a TABLE ( id int, timest datetime, source char(2),
isAQI int, prevAQI char(2), nextAQI char(2))
INSERT #a VALUES
(6694 ,'2015-06-11 08:55:06.000' ,'I' ,1, NULL, 'A'),
(6694 ,'2015-06-11 09:00:00.000' ,'A' ,1, 'I', 'I'),
(6694 ,'2015-06-11 09:11:49.000' ,'C' ,NULL, 'A', 'I'),
(6694 ,'2015-06-11 09:29:06.000' ,'O' ,NULL, 'A', 'I'),
(6694 ,'2015-06-11 09:29:06.000' ,'DT' ,NULL, 'A', 'I'),
(6694 ,'2015-06-11 09:34:11.000' ,'DT' ,NULL, 'A', 'I'),
(6694 ,'2015-06-11 09:34:11.000' ,'O' ,NULL, 'A', 'I'),
(6694 ,'2015-06-11 10:06:27.000' ,'I' ,1, 'A', 'I'),
(6694 ,'2015-06-11 11:25:09.000' ,'DT' ,NULL, 'I', 'I'),
(6694 ,'2015-06-11 18:25:24.000' ,'C' ,NULL, 'I', 'I'),
(6694 ,'2015-06-12 17:57:16.000' ,'I' ,1, 'I', NULL);
SELECT *
FROM #a

See if the following query works for you:
WITH C AS
(
SELECT *,
MAX(goodval) OVER(PARTITION BY id
ORDER BY timest
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS prv,
MIN(goodval) OVER(PARTITION BY id
ORDER BY timest
ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING) AS nxt
FROM #a
CROSS APPLY ( VALUES( CONVERT(VARCHAR(23), timest, 121)
+ CASE WHEN isAQI = 1 THEN source END ) ) AS A(goodval)
)
SELECT id, timest, source,
CASE WHEN prv IS NOT NULL THEN SUBSTRING(prv, 24, 2) END AS prevAQI,
CASE WHEN nxt IS NOT NULL THEN SUBSTRING(nxt, 24, 2) END AS nextAQI
FROM C
ORDER BY id, timest;

Related

Delete query is not working with order by for multiple columns

CREATE TABLE SAMPLE1
(
CN VARCHAR(MAX),
CR VARCHAR(MAX),
DN VARCHAR(MAX),
DR VARCHAR(MAX),
DMR VARCHAR(MAX)
)
INSERT INTO SAMPLE1 VALUES ('C1', 'A', 'D1', '--', '--')
INSERT INTO SAMPLE1 VALUES ('C1', 'B', 'D1', '-A', '--')
INSERT INTO SAMPLE1 VALUES ('C1', 'E', 'D2', '--', '--')
INSERT INTO SAMPLE1 VALUES ('C1', 'C', 'D1', '-A', '--')
INSERT INTO SAMPLE1 VALUES ('C1', 'D', 'D3', '--', '--')
INSERT INTO SAMPLE1 VALUES ('C1', 'F', 'D2', '--', '--')
INSERT INTO SAMPLE1 VALUES ('C1', 'F', 'D2', '-A', '--')
Expected result:
('C1', 'F', 'D2', '-A', '--')
i.e last record from SAMPLE1.
I tried with the following query, but it doesn't work:
DELETE t
FROM
(SELECT
ROW_NUMBER() OVER(PARTITION BY CN
ORDER BY CR, DR, DMR DESC) AS r
FROM SAMPLE1) t
WHERE r > 1
Given that you are using SQL Server, you could place the logic from your current query into a deletable CTE:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY CN
ORDER BY CR DESC, DR DESC, DMR DESC) rn
FROM SAMPLE1
)
DELETE
FROM cte
WHERE rn > 1;

T-SQL how to filter by multiple criteria but prioritize what is returned?

I have the data below.
create table #results (
id int
, result_attr char(1)
, result varchar(100)
)
insert into
#results (id, result_attr, result)
values
(1, 'E', '***ERROR')
, (2, 'E', '***CORRECTED')
, (3, 'E', '***RESULTED')
, (4, 'E', '***AMENDED')
, (4, 'E', 'FOO')
, (5, 'E', 'ERROR***')
, (5, 'E', 'CORPOREAL')
, (6, 'E', '***CORRECTED')
, (7, 'E', '***RESULTED')
, (7, 'E', 'ABUNDANT')
, (7, 'E', 'PLENTITUDE')
, (8, 'E', 'INCORRECT')
, (9, 'A', 'HIGH')
, (10, 'A', 'LOW')
select *
from #results
drop table #results
The complete result set is:
My desired result set is:
This doesn't quite work:
select
res.id
, res.result_attr
, res.result
from #results as res
where
(charindex('***', res.result) > 0 or res.result_attr = 'E')
Tricky part being that I would want to exclude ID #4 with result "FOO" as well as ID #5 with result "CORPOREAL" and ID #7 with results "ABUNDANT" and "PLENTITUDE", but I want to keep ID #8 with result "INCORRECT". All in all, I want to exclude the following:
I've tried some windowing functions and other things, but am a bit stuck on this one. I would appreciate any assistance!
You can use row_number analytical function with conditional ordering as follows:
Select * from
(select
res.id
, res.result_attr
, res.result
, row_number() over (partition by res.id
order by case when charindex('***', res.result) > 0
then 1 else 2 end) as rn
from #results as res
where
(charindex('***', res.result) > 0 or res.result_attr = 'E') t
Where rn = 1
Order by id
Using:
select
res.id
, res.result_attr
, res.result
from results as res
where res.result LIKE '%***%' OR res.result = 'INCORRECT';
db<>fiddle demo

SQL Server: how to form a crosstab

I am using SQL Server 2014.
How can I create a cross-table out of the straight table like the below?
Code here where I try to do the multiple-pivoting.
Sample input
Code here only about the sample data.
GOAL after pivoting and reformatting of the columns by other columns
This logic would work for you:
Create an additional temp table where you store all the possible combinations of Store and City, then left join with your existing table.
From this left join you get the "calculated" string that you will use as a column name (C1_L_xCount, C1_L2_xCount, etc).
Then apply a pivot as below:
select ylabel.colx, ylabel.coly, y.myid, y.week, isnull(y.xCount, 0) xCount, isnull(y.yCount, 0) yCount
into #table
from (
select distinct y3.week, y1.city, y2.store, y1.city + '_' + y2.store + '_xCount' as colx, y1.city + '_' + y2.store + '_yCount' as coly
from #yt y1 cross join #yt y2 cross join #yt y3
) ylabel left join #yt y on y.week = ylabel.week and y.store = ylabel.store and y.city = ylabel.city
select * from #table --this is the additional table, the one used for pivoting
--this is your solution:
select myid
, week
, isnull(C1_L_xCount, 9) C1_L_xCount
, isnull(C1_L2_xCount, 9 ) C1_L2_xCount
, isnull(C2_L_xCount, 0) C2_L_xCount
, isnull(C2_L2_xCount, 0) C2_L2_xCount
, isnull(C1_L_yCount, 0) C1_L_yCount
, isnull(C1_L2_yCount, 0) C1_L2_yCount
, isnull(C2_L_yCount, 0) C2_L_yCount
, isnull(C2_L2_yCount, 0) C2_L2_yCount
from
(
select *
from #table
pivot ( max(xCount) for colx in ( [C1_L_xCount], [C1_L2_xCount],[C2_L_xCount], [C2_L2_xCount])) p
pivot ( max(yCount) for coly in ( [C1_L_yCount], [C1_L2_yCount],[C2_L_yCount], [C2_L2_yCount])) q
where myid is not null
) t
Please check a working demo here.
But, if you need to dynamically add Stores and Cities, you will need to convert this into a dynamic pivot.
We repeat the case such that
CASE WHEN
[City]='C1' AND [Store]='L'
THEN
[xCount]
END
AS 'LC1_xCount',
I changed the valuations a little bit to get more combinations, code here.
or code here:
CREATE TABLE #yt
([MyID] int, [Store] nvarchar(300), [City] nvarchar(300), [Week] int, [xCount] int, [yCount] int)
;
INSERT INTO #yt
([MyID], [Store], [City], [Week], [xCount], [yCount])
VALUES
(1, 'L', 'C1', 1, 96, 7),
(2, 'L', 'C1', 1, 138, 77),
(3, 'L2', 'C1', 1, 37, 744),
(4, 'L', 'C1', 1, 59, 74),
(5, 'L', 'C1', 2, 282,73333),
(6, 'L2', 'C2', 2, 212,7333),
(7, 'L2', 'C2', 2, 78,733),
(8, 'L', 'C2', 2, 97,73),
(9, 'L', 'C2', 3, 60,72222),
(10, 'L2', 'C2', 3, 123,7222),
(11, 'L2', 'C1', 3, 220,722),
(12, 'L2', 'C1', 3, 87,72)
;
select [MyId], [Week], [LC1_xCount], [LC2_xCount], [L2C1_xCount], [L2C2_xCount]
, [LC1_yCount], [LC2_yCount], [L2C1_yCount], [L2C2_yCount]
from
(
select myid, week, store, city, xcount, ycount,
CASE WHEN
[City]='C1' AND [Store]='L'
THEN
[xCount]
END
AS 'LC1_xCount',
CASE WHEN
[City]='C2' AND [Store]='L'
THEN
[xCount]
END
AS 'LC2_xCount',
CASE WHEN
[City]='C1' AND [Store]='L2'
THEN
[xCount]
END
AS 'L2C1_xCount',
CASE WHEN
[City]='C2' AND [Store]='L2'
THEN
[xCount]
END
AS 'L2C2_xCount',
CASE WHEN
[City]='C1' AND [Store]='L'
THEN
[yCount]
END
AS 'LC1_yCount',
CASE WHEN
[City]='C2' AND [Store]='L'
THEN
[yCount]
END
AS 'LC2_yCount',
CASE WHEN
[City]='C1' AND [Store]='L2'
THEN
[yCount]
END
AS 'L2C1_yCount',
CASE WHEN
[City]='C2' AND [Store]='L2'
THEN
[yCount]
END
AS 'L2C2_yCount'
from #yt
GROUP BY myid, week, store,city, xcount, ycount
) src;

SQL: How to fill the rows between 2 specific rows with the same value?

I need to fill/replace the rows with ' - ' between two 'AL' OR two 'MX' with the value 'AL' or 'MX' depending on where the '-' appears. For the purpose of this example I use only 2 'uid' (i actually have more uid). Besides, the table is ordered by uid and code_date column in ASC
For easy understanding, I have this table:
but I will like to have something like this:
I am using SQL Server 2008. Any suggestion on how I can achieve this???
I created the table with the following code:
DECLARE #Customers TABLE
(uid bigint,
code_date date,
Value nchar(10)
)
INSERT INTO #Customers
VALUES (1591, '2016-08-01', ''),
(1591, '2016-08-02', ''),
(1591, '2016-08-03', 'AL'),
(1591, '2016-08-04', '-'),
(1591, '2016-08-05', '-'),
(1591, '2016-08-06', '-'),
(1591, '2016-08-07', '-'),
(1591, '2016-08-08', '-'),
(1591, '2016-08-09', 'AL'),
(1591, '2016-08-10', ''),
(1591, '2016-08-11', 'AL'),
(1591, '2016-08-12', ''),
(1082, '2016-02-01', ''),
(1082, '2016-02-02', ''),
(1082, '2016-02-03', ''),
(1082, '2016-02-04', ''),
(1082, '2016-02-05', 'MX'),
(1082, '2016-02-06', '-'),
(1082, '2016-02-07', '-'),
(1082, '2016-02-08', '-'),
(1082, '2016-02-09', '-'),
(1082, '2016-02-10', '-'),
(1082, '2016-02-11', '-'),
(1082, '2016-02-12', 'MX');
SELECT * FROM #Customers ORDER BY uid, code_date ASC
/* Test Data & Table */
DECLARE #Customers TABLE
(Dates datetime,
Customer integer,
Value integer)
INSERT INTO #Customers
VALUES ('20100101', 1, 12),
('20100101', 2, NULL),
('20100101', 3, 32),
('20100101', 4, 42),
('20100101', 5, 15),
('20100102', 1, NULL),
('20100102', 2, NULL),
('20100102', 3, 39),
('20100102', 4, NULL),
('20100102', 5, 16),
('20100103', 1, 13),
('20100103', 2, 24),
('20100103', 3, NULL),
('20100103', 4, NULL),
('20100103', 5, 21),
('20100104', 1, 14),
('20100104', 2, NULL),
('20100104', 3, NULL),
('20100104', 4, 65),
('20100104', 5, 23) ;
/* CustCTE - This gives us a RowNum to allow us to build the recursive CTE CleanCust */
WITH CustCTE
AS (SELECT Customer,
Value,
Dates,
ROW_NUMBER() OVER (PARTITION BY Customer ORDER BY Dates) RowNum
FROM #Customers),
/* CleanCust - A recursive CTE. This runs down the list of values for each customer, checking the Value column, if it is null it gets the previous non NULL value.*/
CleanCust
AS (SELECT Customer,
ISNULL(Value, 0) Value, /* Ensure we start with no NULL values for each customer */
Dates,
RowNum
FROM CustCte cur
WHERE RowNum = 1
UNION ALL
SELECT Curr.Customer,
ISNULL(Curr.Value, prev.Value) Value,
Curr.Dates,
Curr.RowNum
FROM CustCte curr
INNER JOIN CleanCust prev ON curr.Customer = prev.Customer
AND curr.RowNum = prev.RowNum + 1)
/* Update the base table using the result set from the recursive CTE */
UPDATE trg
SET Value = src.Value
FROM #Customers trg
INNER JOIN CleanCust src ON trg.Customer = src.Customer
AND trg.Dates = src.Dates
/* Display the results */
SELECT * FROM #Customers
declare #x varchar(1000) = ''
update #Customers
set #x = value = (case when #x <> '' and value not in ('-',#x) then '' else #x end) + (case when value = '-' then '' when value = #x then '' else value end)
where value <> ''
For a select query, just use outer apply:
select t.*, coalesce(t.value, t2.value) as new_value
from t outer apply
(select top 1 t2.*
from t t2
where t2.uid = t.uid and t2.code_date < t.code_date and t2.value is not null
order by t2.code_date desc
) t2;
You can use similar logic in an update.
As an update, this would be:
update t
set value = t2.new_value
from t outer apply
(select top 1 t2.*
from t t2
where t2.uid = t.uid and t2.code_date < t.code_date and t2.value is not null
order by t2.code_date desc
) t2
where t.value is null;

Retrieving consecutive rows (and the counts) with the same values

I've got a table with almost 10 million views and would to run this query on the latest million or hundred thousand or so.
Here's a SQL fiddle with example data and input/output: http://sqlfiddle.com/#!9/340a41
Is this even possible?
CREATE TABLE object (`id` int, `name` varchar(7), `value` int);
INSERT INTO object (`id`, `name`, `value`)
VALUES
(1, 'a', 1),
(2, 'b', 2),
(3, 'c', 100),
(4, 'a', 1),
(5, 'b', 2),
(6, 'c', 200),
(7, 'a', 2),
(8, 'b', 2),
(9, 'c', 300),
(10, 'a', 2),
(11, 'b', 2),
(12, 'a', 2),
(13, 'b', 2),
(14, 'c', 400)
;
-- Want:
-- name, max(id), count(id)
-- 'a', 4, 2
-- 'b', 14, 5
-- 'a', 12, 3
If you want the latest and the id is implemented sequentially, then you can do this using limit or top. In SQL Server:
select top 100000 o.*
from object o
order by id desc;
In MySQL, you would use limit:
select o.*
from object o
order by id desc
limit 100000
select name, count(id) cnt, max(id) max_id, max(value) max_v
from
(select
top 1000000 -- MS SQL Server
id,name,value
from myTable
limit 1000000 --mySQL
order by id desc)
group by name
remove line which doesn't match your server.