Cumulative Addition in SQL Server 2008 - database

Sample data in tblData:
RowID SID Staken DateTaken
---------------------------------------------
1 1 1 2014-09-15 14:18:11.997
2 1 1 2014-09-16 14:18:11.997
3 1 1 2014-09-17 14:18:11.997
I would like to get the daywise count of SIDs and also a cumulative sum like
Date ThisDayCount TotalCount
-----------------------------------
2014-09-15 1 1
2014-09-16 10 11
2014-09-17 30 41
This is what I have now in my stored procedure with the start & end date parameters. Is there a more elegant way to do this?
;WITH TBL AS
(
SELECT
CONVERT(date, asu.DateTaken) AS Date,
COUNT(*) AS 'ThisDayCount'
FROM
tblData asu
WHERE
asu.SID = 1
AND asu.STaken = 1
AND asu.DateTaken >= #StartDate
AND asu.DateTaken <= #EndDate
GROUP BY
CONVERT(date, asu.DateTaken)
)
SELECT
t1.Date, t1.ThisDayCount, SUM(t1.ThisDayCount) AS 'TotalCount'
FROM
TBL t1
INNER JOIN
TBL t2 ON t1.date >= t2.date
GROUP BY
t1.Date, t1.ThisDayCount

I am not aware of a more elegant way to do that, other than perhaps with a subquery for your running total. What you have is pretty elegant by T-SQL standards.
But, depending on how many records you have to process and what your indexes look like, this could be very slow. You don't say what the destination of this information is, but if it's any kind of report or web page, I'd consider doing the running total as part of the processing at the destination rather than in the database.

Related

subquery returned more than 1 value table join

I am writing a course project (something like a program for the hotel manager) and I need a little help. I have tables Reservations and Rooms and I need to calculate the amount of payment after the client leaves the room ((End_date - Start_date) * price_per_day), but I'm having trouble getting the price_per_day from the table Rooms.
My query only works if there is one record in the Resertvation table, if there are 2 or more, I get an error "subquery returned more than 1 value" and I don’t know how to fix it (the problem is in this part of the query SELECT price_per_day FROM Rooms AS ro JOIN Reservations AS re ON ro.room_id = re.room_id)
I'm using visual studio 2019 + SQL Server Express LocalDB.
I will be grateful for any help or hint!
UPDATE Reservations
SET Amount_payable = (
DATEDIFF(day, CONVERT(datetime, Start_date, 104), CONVERT(datetime, End_date, 104) * (SELECT price_per_day FROM Rooms AS ro JOIN Reservations AS re ON ro.room_id = re.room_id))
)
WHERE Status = 'Archived'
Table Reservations
reservation_id customer_id room_id start_date end_date status Amount_payable
1 3 3 12.04.2020 05.06.2020 Archived 0
2 2 4 11.04.2020 30.05.2020 Active 0
Table Rooms
reservation_id room_id number_of_persons room_type price_per_day
0 1 3 Double 300
0 2 4 Triple 600
0 3 3 Studio 400
2 4 2 Single 444
you need slightly different approach to resolve the issue.
try the following:
UPDATE re
SET
Amount_payable = (DATEDIFF(day, CONVERT(DATETIME, Start_date, 104), CONVERT(DATETIME, End_date, 104)) * price_per_day)
FROM Reservations re
JOIN Rooms AS ro ON ro.room_id = re.room_id
WHERE STATUS = 'Archived';

I want to get data if records are not present in specific month

I wrote a sql query to get all records happen in specific month
select month(loggingdate),Count(id) from communicationlogs
where clientid=20154 and month(loggingdate) in (1,2,3,4,5,6,7,8,9)
group by month(loggingdate)
7 65
8 5
here records are present in 7th and 8th month. I want to get 0 value for other month numbers like-
1 0
2 0
3 0
4 0
...
This is a standard problem where a calendar table comes in handy. A calendar table, as the name implies, is a table which just stores a sequence of dates. In your particular case, we only need the digits corresponding to the 12 months. Begin the query with the calendar table and then left join to your aggregation query as a subquery.
Note the use of COALESCE below. If a given month appears nowhere in your original query, then its count would show up as NULL in the join, in which case we report zero for that month.
WITH calendar_month AS (
SELECT 1 AS month
UNION ALL
SELECT month +1
FROM
calendar_month
WHERE month +1 <= 12
)
SELECT
t1.month,
COALESCE(t2.cnt, 0) AS cnt
FROM calendar_month t1
LEFT JOIN
(
SELECT
MONTH(loggingdate) as month,
COUNT(id) AS cnt
FROM communicationlogs
WHERE
clientid = 20154 AND
MONTH(loggingdate) IN (1,2,3,4,5,6,7,8,9)
GROUP BY MONTH(loggingdate)
) t2
ON t1.month = t2.month

T sql group by month

I'm trying to group by according to month from datetime
I run below query
select cf.flow_name as 'Process', COUNT(c.case_ID) as 'Case', CONVERT(VARCHAR(10),c.xdate,104) as 'Date'
from cases c inner join case_flow cf on c.case_flow_ID=cf.CF_ID
where project_ID=1 and c.subject_ID=1
group by cf.flow_name,c.xdate
Columns data types as below
flow_name varchar(100)
case_ID int
xdate datetime
Result displays like below if i run above query
Process - Case - Date
Test 1 30.01.2015
Test 1 30.01.2015
analysis 1 19.03.2015
analysis 1 30.03.2015
analysis 1 13.04.2015
analysis 1 16.04.2015
Question:
I need to group by as below (group by according to month for x.date)
Correct Result should be as below
Process - Case - Date
Test 2 30.01.2015 (Because Test has 2 data from 01 month)
analysis 2 19.03.2015 (Because analysis has 2 data from 03 month)
analysis 2 13.04.2015 (Because analysis has 2 data from 04 month)
as above all result should group by month how can i do this according to my query ?
hope you understand my english thanks
SELECT cf_flow,
Count(*),
Min(xdate)
FROM cases c
INNER JOIN case_flow cf
ON c.case_flow_id = cf.cf_id
WHERE project_id = 1
AND c.subject_id = 1
GROUP BY cf_flow,
Dateadd(month, Datediff(month, 0, xdate), 0)

How to auto-add dates in column, SQL Server 2014

I'm very new to SQL Server and I want to have dates from today up to 30 days ahead of todays date in one column, which way is the most considered efficient and "correct" way? ( I'm not asking for code ).
I read that loops should preferably be avoided in SQL Server, is that correct? Also, I thought of solving the date-issue with using a logon trigger (adding 30 days ahead of today whenever a logon happens), anyone know a more efficient and "correct" way?
Thanks
You can use recursive CTE to get sequential dates for next 30 days.
CREATE TABLE Dates
(
allDates DATE
)
;WITH MyCTE
AS (SELECT getdate() AS ddate,
dateadd(day, 30, getdate()) AS lastDate
UNION ALL
SELECT dateadd(day, 1, ddate),
lastDate
FROM MyCTE
WHERE dateadd(day, 1, ddate) <= lastDate)
INSERT INTO Dates(allDates)
SELECT ddate FROM MyCTE
SELECT * FROM Dates
SQL Fiddle Demo
The most efficient way to do this would be a Job. SQL Server Agent provides the ability to run any script you want on any interval you choose. A very simplistic approach would be to create a job which runs nightly and inserts a row for [Today + 30 Days].
I believe you are seeking 30 rows from a query with each row representing a date starting at today, and finishing 30 days after today.
There are many potential solutions for this that don't use a cursor/loop, for example
select
dateadd(day,nums.number,nums.today) as a_date
from (
select
number
, cast(getdate() as date) as today
FROM master.dbo.spt_values as sv
WHERE sv.type = 'P'
AND sv.number BETWEEN 0 and 29
) nums
see: this SQLfiddle demo
Note that query is using master.dbo.spt_values and some prefer not to use this (refer here). So instead you could use a small union all with cross join to generate the rows, or you can use a recursive "common table expression" (CTE) as an alternative.
;WITH
Digits AS (
SELECT 0 AS digit UNION ALL
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL
SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
)
, Tally AS (
SELECT [tens].digit * 10 + [ones].digit AS number
FROM Digits [ones]
CROSS JOIN Digits [tens]
)
select
dateadd(day,nums.number,nums.today) as a_date
from (
select
number
, cast(getdate() as date) as today
FROM tally
WHERE number BETWEEN 0 and 29
) nums
To get todays date + 30 days do this:
select dateadd(dd,30,getdate())

How to select a specific set of rows and then select next row relevant to the original row

I don't know if what i'm looking for it's possible with my current dataset, or if what i'm expecting it's possible at all.
what i am trying to accomplish is to get all rows with status = 2 or 7 get the date and then get the next row with different status to obtain the dateinterval and get the nuber of days that the status had.
DataSet
id_compromiso|fecha |id_actividad|status
-------------+-----------+------------+----------
32 2013-12-10 359 2
32 2013-12-16 380 5
32 2013-12-18 401 7
32 2013-12-24 485 8
58 2013-12-02 248 2
58 2013-12-03 254 2
58 2013-12-10 360 2
58 2013-12-10 378 5
58 2013-12-12 395 2
what have i tried:
SQL query:
WITH pausa AS (
SELECT tmp.id_compromiso, tmp.fecha, MIN(tact.id_actividad) as id_actividad
FROM Actividades as tact
INNER JOIN (
SELECT act.id_compromiso, CAST(act.fecha as date) as fecha
FROM actividades as act
WHERE act.[status]=7
) as tmp
ON(tmp.id_compromiso = tact.id_compromiso AND tmp.fecha = CAST(tact.fecha as date))
WHERE tact.[status]=7
GROUP BY tmp.id_compromiso, tmp.fecha
),
revision AS (
SELECT tmp.id_compromiso, tmp.fecha, MIN(tact.id_actividad) as id_actividad
FROM Actividades as tact
INNER JOIN (
SELECT act.id_compromiso, CAST(act.fecha as date) as fecha
FROM actividades as act
WHERE act.[status]=2
) as tmp
ON(tmp.id_compromiso = tact.id_compromiso AND tmp.fecha = CAST(tact.fecha as date))
WHERE tact.[status]=2
GROUP BY tmp.id_compromiso, tmp.fecha
)
SELECT * FROM revision ORDER BY id_compromiso;
but really running i'm out of ideas on how to get the next item with different status from the table ...
-- First, it extends actividades to include the minimum fecha for the status
-- on the compromiso; this is min(fecha) in the partition by compromiso/status
WITH status_start AS(
SELECT *, MIN(fecha) OVER (PARTITION BY id_compromiso, status) sStart
FROM actividades
),
-- Then, join the extended actividades table with itself (aliased a and b) by compromiso but status 2,7 with status not 2,7
-- (this is the AND a.STATUS IN (2,7) AND b.STATUS NOT IN(2,7) in the join clause)
-- and making sure it's a later status (the a.sStart <b.sStart bit)
-- at this point also calculates the date difference in days
status_start_end AS(
SELECT a.*,b.sStart sEnd, DATEDIFF(d, a.sStart, b.sStart) AS sDiff FROM status_start a
JOIN status_start b ON (a.id_compromiso =b.id_compromiso AND a.STATUS IN (2,7) AND b.STATUS NOT IN(2,7) AND a.sStart <b.sStart))
-- Finaly as the previous query would have day difference in relation to ALL later status, we need to select only the minimum difference
-- as this is when the status actually change. We also need to eliminate duplicates using 'distinct;
-- as it could be many entries for the same status and
-- also many later status.
SELECT DISTINCT id_compromiso, status ,
MIN(sDiff) OVER (PARTITION BY id_compromiso) "Nr. of days in status"
FROM status_start_end
Without knowing more about the context in question it's difficult to provide a fitting answer, but something like this may help:
SELECT TOP 1 id_compromiso, fecha, id_actividad, status
FROM Actividades
WHERE CAST(fecha AS DATE)>( SELECT MAX(CAST(fecha AS DATE))
FROM Actividades
WHERE status IN (2,7))
AND status NOT IN (2,7)
ORDER BY CAST(fecha AS DATE) DESC
I have set up a SQL Fiddle here.

Resources