SQL command tallies totals into 2nd table - sql-server

I have a SQL command that SUMS up incidents from TableA and imports the totals into TableB. Then another command that calculates the totals from B and INSERTS INTO TableC. Is it possible to include in TableC the names of those that have the recorded incidents? (Right now it only SUMS up totals and reports as a whole with no names)
I'll give some examples:
TableB
Day 1
Name | Incidents
Tim | 1
Frank | 2
Jay | 1
Day 2
Name | incidents
Tim | 1
Frank | 1
Jay | 1
TableC
Name | Incidents
Tim | 2
Frank | 3
Jay | 2
TableC continues to record data while TableB will be dropped and re recorded daily.
Here is the SQL command to fill TableB:
SELECT [Name], SUM(TableAColumnA) AS TableBColumnB INTO TableB FROM TableA GROUP BY [Name]
Here is the SQL I've tried to populate TableC:
INSERT INTO TableC(ImportDate, DayofData, Name, ColumnBTalbeB)
SELECT GETDATE() AS ImportDate, DATEADD(day, -1, GETDATE()) AS DayofData,
(SELECT SUM(ColumnBTableB) FROM TableB);
What this does is give NULL value to Name and calculate all incidents recorded in TableB.ColumnB. I basically need to show the names of those that had contributed to the total of incidents into TableC. TableC looks like this:
TableC
Name | Incidents | ImportDate | DayofData
NULL | 4 | today's date/time | yesterday's date/time
Was hoping to do something like this.
TableC
Name | incidents | totalincidents | importdate | dayofdata
Tim | 1 | 4 | today's date/tome | yesterday's date/time
Is this possible or do I need to have it calculate into a whole separate table entirely? or just wishful thinking gone too far?

If you could do without TotalIncidents, you would use GROUP BY:
INSERT INTO TableC(ImportDate, DayofData, Name, Incidents)
SELECT GETDATE() AS ImportDate, DATEADD(day, -1, GETDATE()) AS DayofData, Name, Incidents
FROM (SELECT Name, SUM(ColumnBTableB) AS Incidents
FROM TableB
GROUP BY Name);
Since TotalIncidents can be obtained from other data by query:
SELECT SUM(Incidents) AS TotalIncidents
FROM TableC
WHERE DayOfData BETWEEN CONVERT(datetime, '1/24/2016', 101)
AND CONVERT(datetime, '1/25/2016', 101);
Do you really need to store TotalIncidents as a column? It just adds complexity.

Related

Counting on 2 Date columns

So I've got 2 Date Columns, start and end. What I am trying to accomplish is inventory for each day and this will be going back to the beginning of 2020 so that I have these fields:
Start Date, count of new adds that day, count of closed that day, and count of existing open from previous days. My basic data structure I derive is Start Date, End Date, Request Type (if start date = date of report and end date is null then 'New Add', if end date is not null then 'Work Closed' and if Start Date is less than date of report then 'Existing Open'. The problem is that these depend on relativity between the report date and open/close dates. I need to be able to group by a date and give the counts for each day. I tried these 2 solutions and didn't work like I had hoped for as they're slightly different than my scenario. (Count Function on Multiple Columns by Date (SQL Server) and Get count on two different date columns and group by date). When I boils down I need to do a count by each day based on the current date inventory and the existing stuff from the previous day.
My basic data structure is like this and is fake data:
+----+------------+-----------+---+
| ID | StartDate | EndDate | |
+----+------------+-----------+---+
| 1 | 1/1/2020 | NULL | |
| 2 | 12/1/2019 | 1/1/2020 | |
| 3 | 1/1/2020 | 1/3/2020 | |
| 4 | 12/17/2019 | 1/2/2020 | |
+----+------------+-----------+---+
Expected Result:
+-------------+---------+-----------------+-----------+--+--+------+
| Report Date | NewAdds | ExistingOpen | Closed | | | |
+-------------+---------+-----------------+-----------+--+--+------+
| 1/1/2020 | 2 | 1 | 1 | | | |
| 1/2/2020 | 0 | 1 | 1 | | | |
| 1/3/2020 | 0 | 1 | 1 | | | |
+-------------+---------+-----------------+-----------+--+--+------+
set #report_start = '20200101';
set #report_end = '20200103';
select
d.dt,
count(case when t.start_dt = d.dt then 1 end) as Adds,
count(case when d.dt > t.start_dt and d.dt < t.end_dt as Existing,
count(case when t.end_dt = d.dt then 1 end) as Closed
from T t inner join Dates d on d.dt <= coalesce(t.end_dt, #report_end)
where d.dt between #report_start and #report_end
group by d.dt;
Create a table of dates and join against it. Counting is fairly easy at that point.
This is a bad idea because you need to count up across all dates ever. Also I don't know what null end date means. Apologies if this is sloppy as I typed it on my phone.
This is a begining of a solution that fixes the logic to handle any report date:
If Start_date = report_date and (end_date is null or end_date > report date) then 'New Add'
if end_date is not null and end_date <= report_date then 'Work Closed'
if Start_Date < report_date and (end_date is null or end_date > report_date then 'Existing Open'
You need a case expression that will give you one of the three values.
Once you get it working for a single report date, you can generate a range of report dates using this solution and join it with your table: Generate Dates between date ranges
to implement my solution, add a table where I manage the calendar
you have to do several steps to solve the problem:
establish which tasks are open for each calendar interval (NewAddTask)
calculate total open tasks by interval (TotalNewAddTask)
establish which tasks are close for each calendar interval (ClosedTask)
calculate total close tasks by interval(TotalClosedTask)
calculate a schedule of the interval combination (ExistingOpenCalendar)
establish which tasks are Existing Open for each calendar interval (ExistingOpenDetail)
calculate total Existing Open tasks by interval(TotalExistingOpenTask)
I finally combine all the totals with the calendar
with NewAddTask as
(
SELECT IdCalendar,IdTask
FROM
Calendar CROSS JOIN Task
where StarDate between FirstDate and LastDate
),
TotalNewAddTask as
(
select IdCalendar,count(IdTask) as Total
from NewAddTask
group by IdCalendar
),
ClosedTask as
(
SELECT IdCalendar,IdTask
FROM
Calendar CROSS JOIN Task
where isnull(CloseDate,'2020-12-31') between FirstDate and LastDate
),
TotalClosedTask as
(
select IdCalendar,count(IdTask) as Total
from ClosedTask
group by IdCalendar
),
ExistingOpenCalendar as
(
SELECT
Calendarend.IdCalendar ,
CalendarStart.FirstDate,
Calendarend.LastDate
FROM
Calendar as CalendarStart CROSS JOIN Calendar as Calendarend
where
CalendarStart.FirstDate<Calendarend.LastDate
)
, ExistingOpenDetail as
(
select ExistingOpenCalendar.IdCalendar,Task.IdTask
from ExistingOpenCalendar CROSS JOIN Task
where StarDate between FirstDate and LastDate
and not (isnull(CloseDate,'2020-12-31') between FirstDate and LastDate)
and (CloseDate is null or (CloseDate < LastDate))
)
,TotalExistingOpenTask as
(
select IdCalendar,count(IdTask) as Total
from ExistingOpenDetail
group by IdCalendar
)
select
Calendar.IdCalendar,Calendar.FirstDate ,
isnull(TotalNewAddTask.Total,0)as NewAddTask,
isnull(TotalClosedTask.Total,0)as ClosedTask,
isnull(TotalExistingOpenTask.Total,0)as ExistingOpen
from Calendar
left join TotalNewAddTask on Calendar.IdCalendar=TotalNewAddTask.IdCalendar
left join TotalClosedTask on Calendar.IdCalendar=TotalClosedTask.IdCalendar
left join TotalExistingOpenTask on Calendar.IdCalendar=TotalExistingOpenTask.IdCalendar
this query meets the conditions
in this example you can find example

Update a column with LastExclusionDate

In SQL Server 2012, I have a table t1 where we store a list of excluded product.
I would like to add a column LastExclusionDate to store the date since when the product has been excluded.
Every day the product is inserted into the table if it is excluded. If not there will be no row and the next time when the product will be excluded there will be a gap date with the previous insert.
I would like to find a T-SQL query to update the LastExclusionDate column.
I would like to use it to populate column LastExclusionDate the first time (=initialisation) and use it every day to update the column when we insert a new row
I've tried this query, but I don't know how to get LastExclusionDate!
;WITH Cte AS
(
SELECT
product_id,
CreationDate,
LAG(CreationDate) OVER (PARTITION BY Product_ID ORDER BY CreationDate) AS GapStart,
(DATEDIFF(DAY, LAG(CreationDate) OVER (PARTITION BY Product_id ORDER BY CreationDate), CreationDate) -1) AS GapDays
FROM
#t1
)
SELECT *
FROM cte
Here's some sample data:
+------------+--------------+--------------------------------+
| product_id | CreationDate | LastExclusionDate_(toPopulate) |
+------------+--------------+--------------------------------+
| 100 | 2018-05-01 | 2018-05-01 |
| 100 | 2018-05-02 | 2018-05-01 |
| 100 | 2018-05-03 | 2018-05-01 |
| 100 | 2018-06-01 | 2018-06-01 |
| 100 | 2018-06-02 | 2018-06-01 |
| 200 | 2018-09-01 | 2018-09-01 |
| 200 | 2018-09-02 | 2018-09-01 |
| 200 | 2018-09-17 | 2018-09-17 |
+------------+--------------+--------------------------------+
Thanks
The idea in finding gap-less sequences is to compare the series to a gap-less sequence and find groups of records where the difference of both doesn't change. For example, when the date increases one by one and a row number also does, then the difference between both stays the same and we found a group:
WITH
cte (product_id, CreationDate, grp) AS (
SELECT product_id, CreationDate
, DATEDIFF(day, '19000101', CreationDate)
- ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY CreationDate)
FROM #t1
)
SELECT product_id, CreationDate
, MIN(CreationDate) OVER (PARTITION BY product_id, grp) AS LastExclusionDate
FROM cte
For ongoing daily insertions it can be done with something like this.
INSERT INTO <yourTable>
SELECT
newProduct.[product_id],
newProduct.[creationDate],
isnull(existingProduct.[lastExclusionDate], newProduct.[creationDate]) AS [lastExclusionDate]
FROM
(SELECT <#product_id> AS [product_id], <#createionDate> AS [creationDate]) AS newProduct
LEFT JOIN #temp existingProduct
ON existingProduct.[product_id] = newProduct.product_id
AND existingProduct.[creationDate] = DATEADD(DAY,-1,newProduct.[creationDate])
I've got a demo here http://rextester.com/BDEO23118 . It's a larger than necessary demo because it uses the code above with the data you provided to populate a table row-by-row like you might in a daily update process. It then does individual insertions using this code with some new dates so you can see the way it handles new ranges. (just an FYI, rextester displays result dates in day.month.year hh:mm:ss format, but you can dump the script into management studio and it will output in DATE format)

SQL Server join and insert data

My name is Thorsten and I'm new to SQL Server. Now I am facing a problem after setting a join... I joined two tables, and it worked so far, but I don't have enough knowledge to proceed.
Here is table1:
| Item | validDate | Price |
| ---- | --------- | ----- |
| A | 01.01.2017 | 100 |
| A | 31.03.2017 | 100 |
| A | 01.04.2017 | 120 |
| A | 31.07.2017 | 120 |
Now I want to create a table that includes a dataset for the gap in table1:
| Item | validDate | Price |
| ---- | --------- | ----- |
| A | 01.01.2017 | 100 |
| A | 28.02.2017 | 100 |
| A | 31.03.2017 | 120 |
... and so on.
My idea was to set a join from table1 to a date table, were every month end is included. But I have to insert the gap as well by creating a new dataset. With what code I'll be able to solve this issue?
As mentioned - I'm a beginner, so I hope I was able to describe my problem.
Thanks in advance for help!
Try to make use of below Query :
DECLARE #Table TABLE (Item VARCHAR(2), validDate DATE, Price INT)
INSERT INTO #Table VALUES
('A','2017-01-01',100),
('A','2017-03-31',100),
('A','2017-04-01',120),
('A','2017-07-31',120)
SELECT Item,DATEADD(DD,-1,validDate) AS Date,Price FROM #Table
OUTPUT
Item Date Price
A 2016-12-31 100
A 2017-03-30 100
A 2017-03-31 120
A 2017-07-30 120
I'm not sure if I understand you correctly but to fill the 'gaps' in your table you need to insert last days of each month. Here's the script that will do that for you. Since I'm fairly new in SQL too, this might not be the best solution, but worked for me. Please not that it will not insert records with dates that already exist in your validDate column:
declare #dateVar date = '2017-01-01' -- script will start calculating last day of the month from this date. DON'T modify the day value
declare #yearVar int = '2017' -- script insert months until the end of the year in this variable
declare #endDates table
(
item nvarchar(1),
endOfMonthDate date
)
while datepart(year, #dateVar) = #yearVar
begin
insert into #endDates
(item, endOfMonthDate)
values (
'A',
dateadd(day, -1, dateadd(month, 1, #dateVar))
)
set #dateVar = dateadd(month, 1, #dateVar)
end
insert into dbo.table1
(Item, validDate)
(
select item, endOfMonthDate
from #endDates
where endOfMonthDate not in (
select validDate
from table1)
)
Now, updating the records with the correct prices will be a little tricky. First we set up the Price for the last days of each month based on the price from the beginning of the month.
update dbo.table1
set table1.Price = t2.Price
from table1
left join table1 as t2
on month(table1.validDate) = MONTH(t2.validDate)
And then, we update the rest records that don't have prices with values from previous months:
declare #loopVar int = 0
declare #nullsNumb int = (select sum(case when table1.Price is null then 1 else 0 end) from table1) --calculates number of nulls in the Price column
while #loopVar < #nullsNumb --not so great solution that inserts previous month's price to every record that doesn't have any price at this moment
begin
update dbo.table1
set table1.Price = t2.Price
from table1
left join table1 as t2
on month(table1.validDate) = MONTH(t2.validDate) + 1
where table1.Price is null
set #loopVar = #loopVar + 1
end
Here's how the data in table1 look now when ordered by validDate:
Item validDate Price
A 2017-01-01 100
A 2017-01-31 100
A 2017-02-28 100
A 2017-03-31 100
A 2017-04-01 120
A 2017-04-30 120
A 2017-05-31 120
A 2017-06-30 120
A 2017-07-31 120
A 2017-08-31 120
A 2017-09-30 120
A 2017-10-31 120
A 2017-11-30 120
A 2017-12-31 120
Let me know if I was able to help.

Unable to remove duplicates in SQL Query with JOIN and DISTINCT

I have a sort of abstract question with a real world example. I'm attempting to run a query that has an issue with the tables I am joining.
In my first draft of the query, if I add a Distinct and only have the one Inner Join needed, I sum up values that are correct.
The values I yield needed to be broken into 4 other totals depended on certain values. When I add the table in my query that has those values and add it to my join or where clause, it takes those totals and sums up each iteration of the value with the corresponding value.
My Query:
SELECT DISTINCT SUM(CASE WHEN Tax_Records.TaxValue = '0.06' THEN Bill_Summary.NonSalesTax
WHEN Tax_Records.TaxValue = '0.065' THEN Bill_Summary.NonSalesTax
WHEN Tax_Records.TaxValue = '0.07' THEN Bill_Summary.NonSalesTax
WHEN Tax_Records.TaxValue = '0.075' THEN Bill_Summary.NonSalesTax ELSE 0.0 END)
AS 'UnTaxable Sales'
FROM Order_Records INNER JOIN Bill_Summary ON Order_Records.RowNum = Bill_Summary.OrderNumID
LEFT JOIN Tax_Records ON Order_Records.OZipCode = Tax_Records.tZipCode
WHERE Order_Records.Date Between 'DATE' And 'DATE'
AND Order_Records.cState = 'state'
GROUP BY Tax_Records.TaxValue
My query runs correctly, but I get the wrong totals, if I remove the LEFT JOIN and it's corresponding items in the SELECT Statement i get the correct totals.
The Tax_Records table has no relation to any other table in the database so I know putting that in the Join will cause issues.
I changed my query to see why I'm getting the incorrect totals and it's because it will sum up a value depening on the cases on my select.
For instance there's an Bill_Summary with a value of 5, it will sum up 5 4 times, 1 for each tax value. So I know why it would do that, but I want to know how i can add the information from the Tax Table to my query to derive the 4 values from my original correct totals.
I've tried different JOINS, embedded SELECTs, and CTE's but nothing works correctly.
EDIT: All this data is coming from order's placed by customers.
What we want to see is the total value of Tax Collected from a certain State Tax in a period of 1 month. So for the month of March 1st to April 1st.
All the sales charged with a 6% Tax Rate Equals $50.
All the sales charged with a 6.5% Tax Rate Equals $65.
All the sales charged with a 7% Tax Rate equals $20.
All the Sales charged with a 7.5% Tax Rate equals $15.
If I run a query without joining the Tax_Records table, I get my correct total of $145.
No I want to show the total broken up into the 4 values as shown earlier by combining the Zip Codes found in the Order_Records table with the Zip Codes in the Tax_Records table.
What happens if I do that is let's say for the 7.5% Value, the total of those sales are $15. Where one sale was $8 and another $7, if I join the Tax_Records table, it runs the query to show that the total number of tax collected from the sales is $8 for 6%, 6.5%, 7%, and 7.5% same thing for the $7 order which then now shows my total for 7.5% to be $60 as opposed to $15 which it should be.
You can try like this
select * from demo;
+------+-------+
| id | des |
+------+-------+
| 1 | afgg |
| 2 | aaaaa |
+------+-------+
select * from test;
+------+---------+
| id | name |
+------+---------+
| 2 | aaaaa |
| 1 | assdasa |
+------+---------+
select id as id,des as description,'' as id,'' as name from demo UNION select '' as id ,''as description,id as id,name as name from test;
+------+-------------+------+---------+
| id | description | id | name |
+------+-------------+------+---------+
| 1 | afgg | | |
| 2 | aaaaa | | |
| | | 2 | aaaaa |
| | | 1 | assdasa |
+------+-------------+------+---------+
4 rows in set (0.00 sec)

Efficient Date Comparisons in SQL

I hope this question provides all of the necessary information, but please do request more if anything is unclear. This is my first question on stack overflow so please bear with me.
I am running this query on SQL Server 2005.
I have a large derived dataset (i'll provide a small subset later) which has 4 fields;
ID,
Year,
StartDate,
EndDate
Within this data set the ID may (correctly) appear multiple times with different date combinations.
The question I have is what ways are there to identify if a record is 'new' I.E it's start date does not fall between the start and end date of any other records for the same id.
For an example take the data set below (I hope this table comes out correctly!);
+----+------+------------+------------+
| ID | Year | Start Date | End Date |
+----+------+------------+------------+
| 1 | 2007 | 01/01/2007 | 10/10/2007 |
| 1 | 2007 | 01/01/2007 | 05/04/2007 |
| 1 | 2007 | 05/04/2007 | 08/10/2007 |
| 1 | 2007 | 15/10/2007 | 20/10/2007 |
| 1 | 2007 | 25/10/2007 | 01/01/2008 |
| 2 | 2007 | 01/01/2007 | 01/01/2008 |
| 2 | 2008 | 01/01/2008 | 15/07/2008 |
| 2 | 2008 | 10/06/2008 | 01/01/2009 |
+----+------+------------+------------+
If we say nothing existed before 2007 then Row 1 and Row 6 are 'new' at that time.
Rows 2,3,7 and 8 are not 'new' as they either join the end of a previous record or overlap it to form a continuous date period (take rows 6 and 7 there are no 'breaks' between 01/01/2008 and 01/01/2009)
Row 4 and 5 would be considered a new record as it does not attach directly to the end of the previous period for ID 1 or overlap any of the other periods.
Currently to get this data set I have to put all of my data into temporary tables and then join them together on various fields to remove the records I don't want.
Firstly I remove rows where the startdate equals the enddate of another row for that ID (This would get rid of rows 3 and 7)
Then I remove rows where the the start date is between the startdate and enddate of other records for that ID (this would remove rows 2 and 8)
That would leave me withRows 1,4,5 and 6 as the 'new' records which is correct.
Is there a more efficient way to do this such as in some sort of loop, CTE or cough Cursor?
As per the above, if there is anything unclear don't hesitate to ask and I will try and provide you with the information you request.
Try
;with cte as
(
Select *, row_number() over (partition by id order by startdate) rn from yourtable
)
select distinct t1.*
from cte t1
left join cte t2
on t1.ID = t2.ID
and t1.EndDate>=t2.StartDate and t1.StartDate<=t2.EndDate
and t1.rn<>t2.rn
where t2.ID is null
or t1.rn=1
this should work, if you have a unique identifier for each row:
select * from
tbl t3
left outer join
(
select distinct t1.id as id_inside, t1.recno as recno_inside
from
tbl t1 inner join
tbl t2 on
t1.id = t2.id and
(t1.startdate <> t2.startdate or t1.enddate <> t2.enddate) and
(t1.startdate >= t2.startdate and t1.enddate <= t2.enddate)
) t4 on
t3.id = t4.id_inside and
t3.recno = t4.recno_inside
where
id_inside is null and
recno_inside is null
sqlfiddle

Resources