sqlserver get records count repeating - sql-server

I have one doubt in sql server
get records based on installments
Table :productdetails
CREATE TABLE [dbo].[productdetails](
[productid] [int] NULL,
[Productrstartdate] [date] NULL,
[Productenddate] [date] NULL,
[EMIInstallment] [int] NULL
) ON [PRIMARY]
GO
INSERT [dbo].[productdetails] ([productid], [Productrstartdate], [Productenddate], [EMIInstallment]) VALUES (1, CAST(N'2020-10-02' AS Date), CAST(N'2024-10-02' AS Date), 5)
GO
INSERT [dbo].[productdetails] ([productid], [Productrstartdate], [Productenddate], [EMIInstallment]) VALUES (2, CAST(N'2020-02-10' AS Date), CAST(N'2021-02-10' AS Date), 2)
GO
INSERT [dbo].[productdetails] ([productid], [Productrstartdate], [Productenddate], [EMIInstallment]) VALUES (3, CAST(N'2019-01-10' AS Date), CAST(N'2019-01-10' AS Date), 1)
GO
INSERT [dbo].[productdetails] ([productid], [Productrstartdate], [Productenddate], [EMIInstallment]) VALUES (4, CAST(N'2019-01-18' AS Date), CAST(N'2021-01-18' AS Date), 3)
GO
based on above data i want output like below
Productid |Installmentdate |noofinstallmentcount
1 |2020-10-02 |1
1 |2021-10-02 |2
1 |2022-10-02 |3
1 |2023-10-02 |4
1 |2024-10-02 |5
2 |2020-02-10 |1
2 |2021-02-10 |2
3 |2019-01-10 |1
4 |2019-01-18 |1
4 |2020-01-18 |2
4 |2021-01-18 |3
i tried like below :
DECLARE #MINDATE DATE='2019-01-18'
DECLARE #COUNT INT=10
dECLARE #MAXDATE DATE='2024-10-02'
;WITH ABC
AS
(
SELECT productid ,#MINDATE CalendarDate ,1 as id from [dbo].[productdetails]
UNION ALL
SELECT a.productid ,DATEADD(YEAR,1,a.Productrstartdate ), 1 FROM [dbo].[productdetails] a
join [dbo].[productdetails] b on a.productid=b.productid
WHERE #MINDATE <#MAXDATE )
SELECT * FROM ABC
above query not given expected output
could you please tell me how to write query to achive this task in sql server .

You want a tally here. If you're not going to have a value (much) higher than 10, then just a VALUES clause will work:
SELECT pd.productid,
DATEADD(YEAR, V.I, pd.Productrstartdate) AS Installmentdate,
V.I + 1 AS noofinstallmentcount
FROM dbo.productdetails pd
JOIN (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9))V(I) ON pd.EMIInstallment > V.I;
If you need bigger numbers, just use a bigger tally:
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1 AS I
FROM N N1, N N2, N N3) --1000 rows
SELECT pd.productid,
DATEADD(YEAR, T.I, pd.Productrstartdate) AS Installmentdate,
T.I + 1 AS noofinstallmentcount
FROM dbo.productdetails pd
JOIN Tally T ON pd.EMIInstallment > T.I;

You could create a temp table that mimics the actual productdetails table, then iterate through it, creating an output table that lists the product id with each installment date. Once you have that, you can use ROW_NUMBER() to count the installment numbers. Try this:
SELECT *
INTO #productdetails
FROM productdetails
CREATE TABLE #output (
productid int null
,installmentdate date null)
WHILE (SELECT COUNT(*) FROM #productdetails) > 0
BEGIN
DECLARE #yr int = 0
WHILE (SELECT TOP 1 DATEDIFF(year, DATEADD(year, #yr, Productrstartdate), productenddate) FROM #productdetails) >= 0
BEGIN
INSERT INTO #output (
productid
,installmentdate)
SELECT top 1
productid
,DATEADD(year, #yr, Productrstartdate) installmentdate
FROM #productdetails
SET #yr = #yr + 1
END
DELETE TOP (1) FROM #productdetails
END
SELECT *
,ROW_NUMBER() OVER (PARTITION BY ProductId ORDER BY InstallmentDate asc) NumberOfInstallmentCount
FROM #output
DROP TABLE #output, #productdetails

Related

Update by order using cte not working, why?

I want to change serial of table by batch update.
Since update do not contain order by, I used CTE, with clause, made a data set and issued update on the result, expected that it will do as I will.
But it update by Id not by my ordered set.
what is wrong with this update?
CREATE TABLE [dbo].[Test](
[Id] [int] NOT NULL,
[Serial] [nvarchar](10) NOT NULL
)
insert into Test values
(1, 1001),
(2, 1002),
(3, 1003),
(4, 1004),
(5, 1005),
(6, 1006),
(7, 1003)
declare #serial int, #Id int
set #Id =3
select #serial = Serial from Test WHERE Id=#Id
declare #new_serial nvarchar(10);
select #new_serial = cast(#serial as nvarchar(10));
;with Records as
(
Select Id, Serial
, ROW_NUMBER() over
(
order by serial
) as RN
FROM [Test]
where Id>#Id
)
UPDATE Records set
[Serial] = cast(#new_serial as int),
#new_serial = cast(#new_serial as int)+1
Here is what after insert exists:
+--+----+
|1 |1001|
|2 |1002|
|3 |1003|
|4 |1004|
|5 |1005|
|6 |1006|
|7 |1003|
Here is what we need:
+--+----+
|1 |1001|
|2 |1002|
|3 |1003|
|4 |1005|
|5 |1006|
|6 |1007|
|7 |1004|
Your update statement is wrong. You are updating the cte and then setting a value to a local variable. I'm guessing you expect that the update will be executed row by row, thus setting the value of Serial in each row to the previous value + 1. However, that's not how sql works.
To get your desired output from the input you provided you do not need a cte nor do you need to use row_number. You can simply do this:
DECLARE #Id int = 3
UPDATE Test
SET [Serial] = cast(Serial as int) + 1
WHERE Id > #Id
Check:
SELECT Id, Serial
FROM Test
Results:
Id Serial
1 1001
2 1002
3 1003
4 1005
5 1006
6 1007
7 1004
See a live demo on rextster.
You can either count how many rows in your table first and use a variable or pick a large number for your TOP to select the entire table and then order that by your serial:
;with Records as
(
Select TOP 100000
Id
, Serial
FROM [Test]
where Id>#Id
ORDER BY Serial
)
But it update by Id not by my ordered set. what is wrong with this
update?
You have no ordered set in your code.
CTE is not and cannot be "ordered".
You should use your calculated RN in your update, if you don't, your code does not depend on row_number() at all:
declare #Test table(
[Id] [int] NOT NULL,
[Serial] [nvarchar](10) NOT NULL
)
insert into #Test values
(1, 1001),
(2, 1002),
(3, 1003),
(4, 1004),
(5, 1005),
(6, 1006),
(7, 1003)
declare #serial int, #Id int
set #Id =3
select #serial = Serial from #Test WHERE Id=#Id
--declare #new_serial nvarchar(10);
--select #new_serial = cast(#serial as nvarchar(10));
;with Records as
(
Select Id, Serial
, ROW_NUMBER() over
(
order by serial
) as RN
FROM #Test
where Id>#Id
)
UPDATE Records set
[Serial] = cast(cast(#serial as int) + RN as nvarchar(10))
select *
from #test

Sort by most recent but keep together by another ID column

I am trying to get some sorting and keep together (not really grouping) working.
In my sample data I would like to keep the DealerIDs together, sorted by IsPrimaryDealer DESC, but show the group (ok maybe it is grouping) of dealers by the ones with the most recent entry.
Result set 2 is the closest, but Grant and his brother should be displayed as the first two rows, in that order. (Grant should be row 1, Grants Brother row 2 because Grants Brother was the most recently added)
DECLARE #temp TABLE (
DealerPK int not null IDENTITY(1,1), DealerID int,
IsPrimaryDealer bit, DealerName varchar(50), DateAdded datetime
)
INSERT INTO #temp VALUES
(1, 1, 'Bob', GETDATE() - 7),
(2, 1, 'Robert', GETDATE() - 7),
(3, 1, 'Grant', GETDATE() - 7),
(3, 0, 'Grants Brother', GETDATE() - 1),
(2, 0, 'Roberts Nephew', GETDATE() - 2),
(1, 0, 'Bobs Cousin', GETDATE() - 3)
-- Data As Entered
SELECT * FROM #temp
-- Data Attempt at Row Numbering
SELECT *, intPosition =
ROW_NUMBER() OVER (PARTITION BY IsPrimaryDealer ORDER BY DealerID, IsPrimaryDealer DESC)
FROM #temp
ORDER BY DateAdded DESC
-- Data Attempt By DateAdded
SELECT *, intPosition =
ROW_NUMBER() OVER (PARTITION BY DealerID ORDER BY DateAdded DESC)
FROM #temp
ORDER BY intPosition, DateAdded
Expected Result
PK DID IsPr Name DateAdded
3 3 1 Grant 2015-10-08 17:14:26.497
4 3 0 Grants Brother 2015-10-14 17:14:26.497
2 2 1 Robert 2015-10-08 17:14:26.497
5 2 0 Roberts Nephew 2015-10-13 17:14:26.497
1 1 1 Bob 2015-10-08 17:14:26.497
6 1 0 Bobs Cousin 2015-10-12 17:14:26.497
As requested by OP:
;WITH Cte AS(
SELECT *,
mx = MAX(DateAdded) OVER(PARTITION BY DealerID) FROM #temp
)
SELECT *
FROM Cte
ORDER BY mx DESC, DealerID, IsPrimaryDealer DESC
Hope i understood your question,
This query results expected output :
SELECT Row_number()
OVER (
PARTITION BY DealerID
ORDER BY DealerPK)RN,
DealerPK,
DealerID,
IsPrimaryDealer,
DealerName,
DateAdded
FROM #temp
ORDER BY DealerID DESC

SQL - Filter on dates X number of days apart from the previous

I have a table containing orders. I would like to select those orders that are a certain number of days apart for a specific client. For example, in the table below I would like to select all of the orders for CustomerID = 10 that are at least 30 days apart from the previous instance. With the starting point to be the first occurrence (07/05/2014 in this data).
OrderID | CustomerID | OrderDate
==========================================
1 10 07/05/2014
2 10 07/15/2014
3 11 07/20/2014
4 11 08/20/2014
5 11 09/21/2014
6 10 09/23/2014
7 10 10/15/2014
8 10 10/30/2014
I would want to select OrderIDs (1,6,8) since they are 30 days apart from each other and all from CustomerID = 10. OrderIDs 2 and 7 would not be included as they are within 30 days of the previous order for that customer.
What confuses me is how to set the "checkpoint" to the last valid date. Here is a little "pseudo" SQL.
SELECT OrderID
FROM Orders
WHERE CusomerID = 10
AND OrderDate > LastValidOrderDate + 30
i came here and i saw #SveinFidjestøl already posted answer but i can't control my self after by long tried :
with the help of LAG and LEAD we can comparison between same column
and as per your Q you are looking 1,6,8. might be this is helpful
SQL SERVER 2012 and after
declare #temp table
(orderid int,
customerid int,
orderDate date
);
insert into #temp values (1, 10, '07/05/2014')
insert into #temp values (2, 10, '07/15/2014')
insert into #temp values (3, 11, '07/20/2014')
insert into #temp values (4, 11, '08/20/2014')
insert into #temp values (5, 11, '09/21/2014')
insert into #temp values (6, 10, '09/23/2014')
insert into #temp values (7, 10, '10/15/2014')
insert into #temp values (8, 10, '10/30/2014');
with cte as
(SELECT orderid,customerid,orderDate,
LAG(orderDate) OVER (ORDER BY orderid ) PreviousValue,
LEAD(orderDate) OVER (ORDER BY orderid) NextValue,
rownum = ROW_NUMBER() OVER (ORDER BY orderid)
FROM #temp
WHERE customerid = 10)
select orderid,customerid,orderDate from cte
where DATEDIFF ( day , PreviousValue , orderDate) > 30
or PreviousValue is null or NextValue is null
SQL SERVER 2005 and after
WITH CTE AS (
SELECT
rownum = ROW_NUMBER() OVER (ORDER BY p.orderid),
p.orderid,
p.customerid,
p.orderDate
FROM #temp p
where p.customerid = 10)
SELECT CTE.orderid,CTE.customerid,CTE.orderDate,
prev.orderDate PreviousValue,
nex.orderDate NextValue
FROM CTE
LEFT JOIN CTE prev ON prev.rownum = CTE.rownum - 1
LEFT JOIN CTE nex ON nex.rownum = CTE.rownum + 1
where CTE.customerid = 10
and
DATEDIFF ( day , prev.orderDate , CTE.orderDate) > 30
or prev.orderDate is null or nex.orderDate is null
GO
You can use the LAG() function, available in SQL Server 2012, together with a Common Table Expression. You calculate the days between the customer's current order and the customer's previous order and then query the Common Table Expression using the filter >= 30
with cte as
(select OrderId
,CustomerId
,datediff(d
,lag(orderdate) over (partition by CustomerId order by OrderDate)
,OrderDate) DaysSinceLastOrder
from Orders)
select OrderId, CustomerId, DaysSinceLastOrder
from cte
where DaysSinceLastOrder >= 30 or DaysSinceLastOrder is null
Results:
OrderId CustomerId DaysSinceLastOrder
1 10 NULL
6 10 70
3 11 NULL
4 11 31
5 11 32
(Note that 1970-01-01 is chosen arbitrarily, you may choose any date)
Update
A slighty more reliable way of doing it will involve a temporary table. But the original table tbl can be left unchanged. See here:
CREATE TABLE #tmp (id int); -- set-up temp table
INSERT INTO #tmp VALUES (1); -- plant "seed": first oid
WHILE (##ROWCOUNT>0)
INSERT INTO #tmp (id)
SELECT TOP 1 OrderId FROM tbl
WHERE OrderId>0 AND CustomerId=10
AND OrderDate>(SELECT max(OrderDate)+30 FROM tbl INNER JOIN #tmp ON id=OrderId)
ORDER BY OrderDate;
-- now list all found entries of tbl:
SELECT * FROM tbl WHERE EXISTS (SELECT 1 FROM #tmp WHERE id=OrderId)
#tinka shows how to use CTEs to do the trick, and the new windowed functions (for 2012 and later) are probably the best answer. There is also the option, assuming you do not have a very large data set, to use a recursive CTE.
Example:
declare #customerid int = 10;
declare #temp table
(orderid int,
customerid int,
orderDate date
);
insert into #temp values (1, 10, '07/05/2014')
insert into #temp values (2, 10, '07/15/2014')
insert into #temp values (3, 11, '07/20/2014')
insert into #temp values (4, 11, '08/20/2014')
insert into #temp values (5, 11, '09/21/2014')
insert into #temp values (6, 10, '09/23/2014')
insert into #temp values (7, 10, '10/15/2014')
insert into #temp values (8, 10, '10/30/2014');
with datefilter AS
(
SELECT row_number() OVER(PARTITION BY CustomerId ORDER BY OrderDate) as RowId,
OrderId,
CustomerId,
OrderDate,
DATEADD(day, 30, OrderDate) as FilterDate
from #temp
WHERE CustomerId = #customerid
)
, firstdate as
(
SELECT RowId, OrderId, CustomerId, OrderDate, FilterDate
FROM datefilter
WHERE rowId = 1
union all
SELECT datefilter.RowId, datefilter.OrderId, datefilter.CustomerId,
datefilter.OrderDate, datefilter.FilterDate
FROM datefilter
join firstdate
on datefilter.CustomerId = firstdate.CustomerId
and datefilter.OrderDate > firstdate.FilterDate
WHERE NOT EXISTS
(
SELECT 1 FROM datefilter betweens
WHERE betweens.CustomerId = firstdate.CustomerId
AND betweens.orderdate > firstdate.FilterDate
AND datefilter.orderdate > betweens.orderdate
)
)
SELECT * FROM firstdate

Select alternate rows from SQL Server table

I am working with SQL Server 2008. I have a table which does not contain any unique columns; how to get alternate rows from it?
SQL Server table:
+-----+--------+
| id | name |
|-----+--------|
| 1 | abc |
| 2 | pqr |
| 2 | pqr |
| 3 | xyz |
| 4 | lmn |
| 5 | efg |
| 5 | efg |
+-----+--------+
As we've to come with at least one working suggestion with the question, I've tried below code; which is not so proper technique when fetching from a huge amount of data.
Trial:
create table #tmp
(
id int, name varchar(10), srNo int
)
insert into #tmp
select
id, name,
ROW_NUMBER() OVER (ORDER BY id) % 2 as srNo --,alternate rows
from
Employee
select *
from #tmp
where srNo = 1 --or srNo = 0
Above query gives out alternate rows i.e. 1st, 3rd, 5th OR 2nd, 4th, 6th etc.
Please help me out with proper way without #tmp to achieve the goal!
You can just use your select statement as an in-line view. You don't need the #tmp table.
select t.id, name
from (select id, name, ROW_NUMBER() over (order by id) as srNo from Employee) t
where (t.srNo % 2) = 1
SqlFiddle
--To fetch ALTERNATE records from a table (EVEN NUMBERED)
Select * from TableName where ColumnName % 2 = 0
For Eg : select * from HumanResources.Employee where BusinessEntityID % 2 = 0
--To fetch ALTERNATE records from a table (ODD NUMBERED)
Select * from TableName where ColumnName % 2 = 1
For Eg : select * from HumanResources.Employee where BusinessEntityID % 2 = 1
I'm taking student as a table name.
Here is my answer ->
For Even Row Number -
> SELECT id from (SELECT rowno, id from student) where mod(rowno,2)=0
For Odd Row Number -
> SELECT id from (SELECT rowno, id from student) where mod(rowno,2)=1
Same also can be achieved using having clause; but it adds group by task:
SELECT id, name
FROM (SELECT id, name, ROW_NUMBER()over(order by id) AS srNo FROM Employee) x
GROUP BY srNo, id, name
HAVING (srNo % 2) = 0
You can just use your select statement as an in-line view. You don't need the #tblCities table.
select tbl1.CityID,tbl1.CityName from (select ROW_NUMBER() over(order by CityID asc) as row_no,CityID,CityName from tblCities) as tbl1 where tbl1.row_no%2=1
declare #t table
(
id int,
name nvarchar(20)
)
insert into #t
Select 1, 'abc'
union all
Select 2, 'pqr'
union all
Select 2, 'pqr'
union all
Select 3, 'xyz'
union all
Select 4, 'lmn'
union all
Select 5, 'efg'
union all
Select 2, 'efg'
Select * from(
Select *, row_number() over(order by id) as rnum from #t ) t where rnum % 2 <> 0
create table t (id bigint NOT NULL, input_1 boolean not null, data_gps timestamp(0) not null);
insert into t (id, input_1,data_gps) values
(1, false , '2022-01-01 15:42:07'),
(2, true , '2022-01-02 15:42:07'),
(3, true , '2022-01-03 15:42:07'),
(4, false , '2022-01-04 15:42:07'),
(5, true , '2022-01-05 15:42:07'),
(6, true , '2022-01-06 15:42:07'),
(7, true , '2022-01-07 15:42:07'),
(8, true , '2022-01-08 15:42:07'),
(9, false , '2022-01-09 15:42:07'),
(10 ,true , '2022-01-10 15:42:07'),
(11, true , '2022-01-11 15:42:07'),
(12, true , '2022-01-12 15:42:07'),
(13, false , '2022-01-13 15:42:07'),
(14, true , '2022-01-14 15:42:07');
you will have
Here is the query that will group by value change
select input_1, min(data_gps) as mind, max(data_gps) as maxd
from (
select input_1, data_gps,
row_number() over (order by data_gps)
- row_number() over (partition by input_1 order by data_gps) as grp
from t
) as tmp
group by input_1, grp
order by min(data_gps);
The results
DEMO
https://dbfiddle.uk/6Ajy3H5O

Update table with constrained values from another table

I'm fairly new to SQL and tried solving this problem for about two days now, to no avail.
The question is, how can I update values in table Table_1 from values in Table_2, considering only specific subset of rows in Table_2, and without using cursors if possible.
More specifically, I have these two tables:
CREATE TABLE [dbo].[Table_1](
[ID] [int] NOT NULL,
[LastAmount] [int] NOT NULL,
[LastDate] [datetime] NOT NULL
) ON [PRIMARY];
CREATE TABLE [dbo].[Table_2](
[ID] [int] NOT NULL,
[Amount] [int] NOT NULL,
[Date_] [datetime] NOT NULL
) ON [PRIMARY];
In the tables there are following values:
INSERT INTO [dbo].[Table_1]
VALUES (1, 0, CONVERT(DATETIME, '19000101', 112)),
(2, 0, CONVERT(DATETIME, '19000101', 112));
INSERT INTO [dbo].[Table_2]
VALUES (1, 10, CONVERT(DATETIME, '19750101', 112)),
(1, 20, CONVERT(DATETIME, '19500101', 112)),
(1, 15, CONVERT(DATETIME, '20000101', 112)),
(2, 30, CONVERT(DATETIME, '20100101', 112));
The point is to update values in Table_1 where ID matches Table_2.
Table_1.LastAmount should get Table_2.Amount with the most recent Table_2.Date_. Similarly for Table_1.LastDate it should get Table_2.Date_ where the date is the most recent for that specific ID.
So, Table_1 before update:
ID |LastAmount |LastDate
----|-----------|--------
1 |0 |1900-01-01 00:00:00.0000
2 |0 |1900-01-01 00:00:00.0000
Table_2:
ID |Amount |Date
----|-----------|--------
1 |10 |1975-01-01 00:00:00.0000
1 |20 |1950-01-01 00:00:00.0000
1 |15 |2000-01-01 00:00:00.0000
2 |30 |2010-01-01 00:00:00.0000
Table_1 after update:
ID |LastAmount |LastDate
----|-----------|--------
1 |15 |2000-01-01 00:00:00.0000
2 |30 |2010-01-01 00:00:00.0000
I tried all kinds of UPDATE with INNER JOIN, or using an inline SELECT when assigning the value, but none of them worked. Many thanks in advance.
An update from is useful in this situation. row_number() is used here with the source table to determine the most recent entry in Table_2.
update
Table_1
set
LastAmount = T2.Amount
,LastDate = T2.Date_
from
(
select
ID
,Amount
,Date_
,row_number() over (partition by ID order by Date_ desc) as RowNumber
from
Table_2
) T2
where
Table_1.ID = T2.ID
and T2.RowNumber = 1
Do like this:
Update Table_1
Set LastAmount = Table_2.Amount,
LastDate = Table_2.[Date_]
from (SELECT ID, MAX([Date_]) as MaxDate FROM TABLE_2 GROUP BY ID) AS Max_Table2
inner join Table_2 on Max_Table2.MaxDate = Table_2.[Date_] and Max_Table2.id = Table_2.id
inner join table_1 on table_1.id = Table_2.id
Basically you have filter the table2 to get the max date and join everything up to find the lastdate and amount.
It's a simple use of ROW_NUMBER() and a CTE to do this:
;With Ordered as (
select ID,Amount,Date_,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Date_ DESC) rn
from Table_2
)
update t1
set LastAmount = o.Amount,
LastDate = o.Date_
from
Table_1 t1
inner join
Ordered o
on
t1.ID = o.ID and
o.rn = 1
(You could write it as a subquery rather than a CTE, but I picked a CTE in this example)

Resources