Calculate previous SUM value - sql-server

I have 2 SQL Server 2005 tables: Names and Scores
Names table:
NameID, Name, Age
1, 'John', 23
2, 'Ryan', 20
Scores table:
ScoreID, NameID, ScoreDate, ScoreValue
1, 1, 01/01/2011, 250
2, 1, 02/01/2011, 300
3, 1, 03/01/2011, 100
4, 2, 01/01/2011, 150
5, 2, 02/01/2011, 350
6, 2, 03/01/2011, 200
I want to get for a given month:
Name, Age, current ScoreValue, sum(ScoreValue) for previous months
Something like this form the month of february:
John, 23, 300, 550
Ryan, 20, 350, 500

Think this is what you want:
select n.Name,
s1.ScoreId,
s1.nameId,
s1.ScoreValue,
sum(s2.ScoreValue) Prevmonths
from names n
inner join scores s1 on n.NameId = s1.NameId
left join scores s2 -- make left join in case no previous month
on s1.NameId = s2.NameId
and s1.ScoreDate >= s2.ScoreDate -- >= means include current onth
group by n.Name,
s1.ScoreId,
s1.nameId,
s1.ScoreValue
GJ

Related

Insert dummy rows to fill missing values into a SQL Table

I have this SQL Server table table1 which I want to fill with dummy rows per acct up to latest previous month end date period e.g now would be up to 2021-06-30.
In this example, acct 1 has n number of rows which ends at 2020-05-31, and I want to insert dummy rows with same values for acct and amt with begin_date and end_date incrementing by 1 month up to 06-30-2021.
Let's assume acct 2 already ends at 06-30-2021 so this doesn't need dummy rows to be inserted.
acct,amt,begin_date,end_date
1 , 10, 2020-04-01, 2020-04-30
1 , 10, 2020-05-01, 2020-05-31
2 , 50, 2021-05-01, 2021-05-31
2 , 50, 2021-06-01, 2021-06-30
So for acct 1, I want n number of rows to be inserted from last period of 2020-05-31 up to previous month end which is now 06-30-2021 and I want the amt and acct to remain same. So it would look like this below:
acct,amt,begin_date,end_date
1 , 10, 2020-04-01, 2020-04-30
1 , 10, 2020-05-01, 2020-05-31
1 , 10, 2020-06-01, 2020-06-30
1 , 10, 2020-07-01, 2020-07-31
.............................
.............................
1 , 10, 2021-06-01, 2021-06-30
Based on some data anamolies, I realize I need another condition to the solution. Suppose another column type was added to the table1. So acct and type would be the composite key that identifies each related row hence acct 2 type A and acct 2 type B are not related. So we have the updated table:
acct,type,amt,begin_date,end_date
1, A, 10, 2020-04-01, 2020-04-30
1, A, 10, 2020-05-01, 2020-05-31
2, A, 50, 2021-05-01, 2021-05-31
2, A, 50, 2021-06-01, 2021-06-30
2, B, 50, 2021-01-01, 2021-01-31
2, B, 50, 2021-02-01, 2021-02-28
I would now need dummy rows to be created for acct 2 type B up to 2021-06-30. We already know acct 2 type A would be ok since it already has rows up to 2021-06-30
You can generate the rows using a recursive CTE:
with cte as (
select acct, amt,
dateadd(day, 1, end_date) as begin_date,
eomonth(dateadd(day, 1, end_date)) as end_date
from (select t.*,
row_number() over (partition by acct order by end_date desc) as seqnum
from t
) t
where seqnum = 1 and end_date < '2021-06-30'
union all
select acct, amt, dateadd(month, 1, begin_date),
eomonth(dateadd(month, 1, begin_date))
from cte
where begin_date < '2021-06-01'
)
select *
from cte;
You can then use insert to insert these rows into a table. Or use union all if you simply want a result set with all the rows.
Here is a db<>fiddle.

Find items with all modifications successfully completed with multiple entries possible per item

I am working on a query to find all orders that have had all the necessary modifications completed and the work item where the last modification was complete. The table that keeps track of all work done on the order can have multiple entries per item. And no, I unfortunately do not have the ability to modify the schema.
Orders Table:
OrderId(int), CustomerId(int), OrderDate(DateTime)
1, 58, '2021-01-01'
2, 75, '2021-01-01'
3, 78, '2021-01-01'
4, 50, '2021-01-01'
Work Table:
WorkId(int), OrderId(int), Mod1Completed(bit), Mod2Completed(bit), Mod3Completed(bit), ModDate (DateTime), ModBy (int)
1005, 1, 0, 1, 1, '2021-02-01', 685
1006, 1, 1, 1, 0, '2021-02-03', 875
1007, 2, 0, 1, 0, '2021-02-01', 211
1008, 3, 1, 1, 1, '2021-01-15', 669
Sample output:
1006, 1, 1, 1, 0, '2021-02-03', 875
1008, 3, 1, 1, 1, '2021-01-15', 669
I have the following query that I think is correct (still testing) but it seems clunky and am trying to improve it. For what it's worth, the Work table is regularly purged and would not have massive amounts of data (most likely < 100 rows at any particular time)
WITH AnySuccessful AS(
SELECT * FROM Work WITH (NOLOCK) WHERE Mod1Completed = 1
OR Mod2Completed = 1
OR Mod3Completed = 1
),
SuccessfulCount AS(
SELECT OrderId,
MAX(s.ModDate) AS ModDate ,
Max(CAST(Mod1Completed as int)) + Max(CAST(Mod2Completed as int)) + Max(CAST(Mod3Completed as int)) AS Successes
FROM AnySuccessful s
GROUP BY OrderId
),
AllSuccessful AS(
Select S.OrderId, WorkID, sc.Successes From AnySuccessful S
Inner Join SuccessfulCount sc on s.OrderId=sc.OrderId and s.ModDate = sc.ModDate AND sc.Successes=3
)
Select w.* from Work w
inner join AllSuccessful ASF on W.OrderId = ASF.OrderId AND W.WorkId = ASF.WorkId
SQLFiddle
This might be easier to follow.. assuming the workid and moddate are always in order
select OrderId
, max(WorkId) as WorkId
, max(convert(int, Mod1Completed)) as Mod1Completed
, max(convert(int, Mod2Completed)) as Mod2Completed
, max(convert(int, Mod3Completed)) as Mod3Completed
, max(ModDate) as ModDate
from Work
group by OrderId
having max(convert(int, Mod1Completed)) + max(convert(int, Mod2Completed)) + max(convert(int, Mod3Completed)) = 3

for sql server, I want to select all of the records that has changed

for example, my table has a record for each date, and each date's record could be same as the previous date record, could be different. my case is from date 1 to date 3, all of the record are same, and then date 4, the record is changed, date 5 the record is changed too, but it changed back to same as date 3. Now I want to a way to query the table and get the records of date 1, date 4 and date 5. Any idea, how to do it? Thanks
I read the issue above, is that a) you take daily logs of all rows, and b) you want to report on any row that is different from the previous day's.
SQL Server has a great function for dealing with differences across a large number of columns - EXCEPT. It also has the advantage of treating NULLs as distinct values - so a change from something to NULL, or vice versa, counts as a change. This is not true for most equality/inequality checks.
Here is a version where I create a daily snapshot of some fields from a 'users' table.
The SELECT query finds all rows from the log, except where the previous entry in the log is the same.
CREATE TABLE #UserLog (LogDate date, UserID int, UserName nvarchar(100), UserEmail nvarchar(100), LastLogonDate datetime, PRIMARY KEY (LogDate, UserID));
INSERT INTO #UserLog (LogDate, UserID, UserName, UserEmail, LastLogonDate) VALUES
('20201011', 1, 'Bob', NULL, '20201009 15:38'),
('20201012', 1, 'Bob', NULL, '20201009 15:38'),
('20201013', 1, 'Bob', 'Bob#gm.com', '20201012 09:15'),
('20201014', 1, 'Bob', 'Bob#gm.com', '20201013 19:02'),
('20201015', 1, 'Bob', 'Bob#gm.com', '20201013 19:02'),
('20201017', 1, 'Bob', 'Bob#gm.com', '20201013 19:02'),
('20201013', 2, 'Pat', 'Pat#hm.com', NULL),
('20201014', 2, 'Pat', 'Pat#hm.com', NULL),
('20201015', 2, 'Pat', 'Pat#hm.com', '20201014 20:55'),
('20201017', 2, 'Pat', 'Pat#hm.com', '20201016 13:22');
SELECT LogDate, UserID, UserName, UserEmail, LastLogonDate
FROM #UserLog
EXCEPT
SELECT LEAD(LogDate) OVER (PARTITION BY UserID ORDER BY LogDate), UserID, UserName, UserEmail, LastLogonDate
FROM #UserLog
ORDER BY UserID, LogDate;
In the 'EXCEPT' segment, it basically gets the data for each given row, then changes the date to the next date in sequence for that user e.g., it turns
('20201012', 1, 'Bob', NULL, '20201009 15:38'),
into
('20201013', 1, 'Bob', NULL, '20201009 15:38'),
As this is not the same as the actual row for Bob on the 13th, the row in the top part of the statement shows.
My initial test run of this simply had a DATEADD(day, 1, Logdate) in the EXCEPT portion, and that would show all rows that were different from yesterday's. However, the updated version above allows for breaks in the sequence (e.g., in the above, the logging failed on the 16th).
Here's a DB<>fiddle with the code above.
UPDATE - data posted in comment in another answer.
Here's a version with that data.
CREATE TABLE #tLog (LogDate date, v_1 int, v_2 varchar(100), v_3 int, v_4 varchar(10), v_5 int, v_6 varchar(10));
INSERT INTO #tLog (Logdate, v_1, v_2, v_3, v_4, v_5, v_6) VALUES
('20200101', 100, 'test_1', 0, '123', 120, 'JJ'),
('20200102', 100, 'test_1', 0, '123', 120, 'JJ'),
('20200103', 100, 'test_1', 0, '123', 120, 'JJ'),
('20200104', 101, 'test_1', 1, '123', 120, 'JJ'),
('20200105', 100, 'test_1', 0, '123', 120, 'JJ'),
('20200106', 101, 'test_1', 1, '12345', 120, 'JJ'),
('20200107', 101, 'test_1', 1, '12345', 120, 'JJ'),
('20200108', 101, 'test_2', 2, '12345', 200, 'JJ'),
('20200109', 101, 'test_1', 1, '12345', 120, 'TT'),
('20200110', 100, 'test_1', 0, '123', 120, 'JJ');
SELECT LogDate, v_1, v_2, v_3, v_4, v_5, v_6
FROM #tLog
EXCEPT
SELECT LEAD(LogDate) OVER (ORDER BY LogDate), v_1, v_2, v_3, v_4, v_5, v_6
FROM #tLog
ORDER BY LogDate;
And here's a copy of the results of the above. Note that only on the 2nd, 3rd and 7th did the data not change from the previous day.
LogDate v_1 v_2 v_3 v_4 v_5 v_6
--------------- ----------------------------
2020-01-01 100 test_1 0 123 120 JJ
2020-01-04 101 test_1 1 123 120 JJ
2020-01-05 100 test_1 0 123 120 JJ
2020-01-06 101 test_1 1 12345 120 JJ
2020-01-08 101 test_2 2 12345 200 JJ
2020-01-09 101 test_1 1 12345 120 TT
2020-01-10 100 test_1 0 123 120 JJ
Note that I have removed the 'PARTITION BY' in the LEAD as there are no real partitions - it's just one row after the next. However there's a distinct chance you may need this when it comes to actual data.
Here's a DB<>fiddle with both the original and this cut-down one with the OP's data.

SQL select where group by order by distinct

I got a one table Search, and columns are:
id(int), title(nvarchar), count(int), expression(nvarchar)
with values:
1, aaa, 5, car
2, aaa, 5, car poster
3, bbb, 8, car magazine
4, bbb, 8, car tv
5, ccc, 12, boat
6, ccc, 12 , boat tv
I need the following result
4, bbb, 8, car tv
3, bbb, 8, car magazine
2, aaa, 5, car poster
1, aaa, 5, car
or better result whatever from 4,3 and 2,1
like this:
3, bbb, 8, car magazine
2, aaa, 5, car poster
I tried this:
SELECT title
FROM
(SELECT *
FROM Search
ORDER BY count DESC)
WHERE expression
LIKE '%car%'
GROUP BY title
I have also tried a DISTINCT
Use a CTE with the ROW_NUMBER function to find the matching rows and order them by descending count along with id for a tie breaker. Then select the first numbered row within each partition (title).
WITH cteFindCar AS (
SELECT id, title, count, expression,
ROW_NUMBER() OVER(PARTITION BY title ORDER BY count DESC, id) AS RowNum
FROM search
WHERE expression LIKE '%car%'
)
SELECT id, title, count, expression
FROM cteFindCar
WHERE RowNum = 1;

Add new rows to resultset in MSSQL

I am running a SQL query in MSSQL 2008 R2 which should always return a consistent resultset, meaning that all dates within a selected date range should be shown, although there are no rows/values in the database for a particular date within the date range. It should for example look like this for the dates 2013-07-03 - 2013-07-04 when there are values for id 1 and 2.
Scenario 1
Date-hour, value, id
2013-07-03-1, 10, 1
2013-07-03-2, 12, 1
2013-07-03-...
2013-07-03-24, 9, 1
2013-07-04-1, 10, 1
2013-07-04-2, 10, 1
2013-07-04-...
2013-07-04-24, 10, 1
2013-07-03-1, 11, 2
2013-07-03-2, 12, 2
2013-07-03-...
2013-07-03-24, 9, 2
2013-07-04-1, 10, 2
2013-07-04-2, 12, 2
2013-07-04-...
2013-07-04-24, 10, 2
However, if id 2 is missing values for 2013-07-04, I will normally only get a resultset which looks like this:
Scenario 2
Date-hour, value, id
2013-07-03-1, 10, 1
2013-07-03-2, 12, 1
2013-07-03-...
2013-07-03-24, 9, 1
2013-07-04-1, 10, 1
2013-07-04-2, 10, 1
2013-07-04-...
2013-07-04-24, 10, 1
2013-07-03-1, 11, 2
2013-07-03-2, 12, 2
2013-07-03-...
2013-07-03-24, 9, 2
Scenario 2 will create an inconsistent resultset which will affect the output. Is there any way to make the SQL query always return as scenario 1 even when there are missing values, so at least to return NULL if there are no values for a specific date within the date range. If the resultset returns id 1 and 2 then all dates for id 1 and 2 should be covered. If id 1, 2 and 3 are returned then all dates for id 1, 2 and 3 should be covered.
I have two tables which look like this:
tbl_measurement
id, date, hour1, hour2, ..., hour24
tbl_plane
planeId, id, maxSpeed
The SQL query I am running look like this:
SELECT DISTINCT hour00_01, hour01_02, mr.date, mr.id, maxSpeed
FROM tbl_measurement as mr, tbl_plane as p
WHERE (date >= '2013-07-03' AND date <= '2013-07-04') AND p.id = mr.id
GROUP BY mr.id, mr.date, hour00_01, hour01_02, p.maxSpeed
ORDER BY mr.id, mr.date
I have been looking around quite a bit, and perhaps PIVOT tables are the way to solve this? Could you please help me out? I would appreciate if you can help me out with how to write the SQL query for this purpose.
You can use a recursive CTE to generate a list of dates. If you cross join that with planes, you get one row per date per plane. With a left join, you can link in measurements if they exist. A left join will leave the row even if no measurement is found.
For example:
declare #startDt date = '2013-01-01'
declare #endDt date = '2013-06-30'
; with AllDates as
(
select #startDt as dt
union all
select dateadd(day, 1, dt)
from AllDates
where dateadd(day, 1, dt) <= #endDt
)
select *
from AllDates ad
cross join
tbl_plane p
left join
(
select row_number() over (partition by Id, cast([date] as date) order by id) rn
, *
from tbl_measurement
where m.inputType = 'forecast'
) m
on p.Id = m.Id
and m.date = ad.dt
and m.rn = 1 -- Only one per day
where p.planeType = 3
option (maxrecursion 0)

Resources