Count(*) automatically rounds - sql-server

I have a query where I am trying to determine what percentage of events happen on certain days and I'm getting nothing but zeroes back. I think (but am not sure) that something is causing my query to round. This is happening to me in SQL Server but not MySQL.
/* create the event table */
create table event (id int
, dayOf datetime
, description varchar(32)
);
/* add some events */
insert into event( id, dayOf, description ) values
( 1, '2018-01-01', 'Thing 1'),
( 2, '2018-01-01', 'Thing 2'),
( 3, '2018-01-02', 'Thing 3'),
( 4, '2018-01-02', 'Thing 4'),
( 5, '2018-01-03', 'Thing 5');
/* try to get % of events by day, but actually get zeroes */
select event_daily.dayOf, event_daily.cnt, event_total.cnt,
event_daily.cnt / event_total.cnt as pct_daily /* this is the zero */
from ( select dayOf, count(*) as cnt from event group by dayOf ) event_daily
, ( select count(*) as cnt from event ) event_total;
Anticipated result:
DateOf cnt cnt pct_daily
1/1/2018 2 5 0.40
1/2/2018 2 5 0.40
1/3/2018 1 5 0.20
Actual result:
DateOf cnt cnt pct_daily
1/1/2018 2 5 0
1/2/2018 2 5 0
1/3/2018 1 5 0
Any help would be much appreciated!

That is because SQL Server performs integer division, you can convert it into float first with CAST
select event_daily.dayOf, event_daily.cnt, event_total.cnt,
CAST(event_daily.cnt AS float) / CAST(event_total.cnt AS float) as pct_daily
from ( select dayOf, count(*) as cnt from event group by dayOf ) event_daily
, ( select count(*) as cnt from event ) event_total;

Try the below approach
declare #TotalCount DECIMAL(18, 2)
select #TotalCount = count(*) from #event
select
a.dayOf, a.DailyCount, a.TotalCount, CONVERT(DECIMAL(18, 2), (A.DailyCount/A.TotalCount)) AS pct_daily
FROM
(select
dayOf, Count(Id) AS DailyCount, #TotalCount as TotalCount
from
#event
group by
dayOf ) a

Related

Group records based on time interval starting from timestamp of first record in each group

Struggling with this; need to group records within a specific time interval starting from the first timestamp (FREEZE_TIME) - but the first record outside the first group is the starting point for the time interval for the next group and so on. Expected result, THAW_COUNT, is the count of all groups for a PARENT_SAMPLE_ID. So for table:
SAMPLE_ID
FREEZE_TIME
PARENT_SAMPLE_ID
1
null
null
2
2015-11-27 10:23:10
1
3
2015-11-27 10:59:23
1
4
2015-11-27 11:05:43
1
5
2015-11-27 12:53:48
1
6
2015-11-27 13:42:25
1
I would like to get a result of:
PARENT_SAMPLE_ID
THAW_COUNT
1
2
So sample_id:s 2,3 and 4 should be in the same group and sample id:s 5 and 6 are in the next group.
I have tried something like:
with SampleList as
(
select PARENT_SAMPLE_ID, FREEZE_TIME,
ROW_NUMBER() OVER (partition by PARENT_SAMPLE_ID order by FREEZE_TIME asc) RN
from
SAMPLE
)
,
FirstSample as
(
select PARENT_SAMPLE_ID, FREEZE_TIME
from SampleList
where RN = 1
)
,
SelectedSample as
(
select
s.PARENT_SAMPLE_ID,
ABS(DATEDIFF(MINUTE, s.FREEZE_TIME, sFirst.FREEZE_TIME))/60 DiffToFirst
from SampleList s
inner join FirstSample sFirst ON s.PARENT_SAMPLE_ID = sFirst.PARENT_SAMPLE_ID
group by s.PARENT_SAMPLE_ID, ABS(DATEDIFF(MINUTE, s.FREEZE_TIME, sFirst.FREEZE_TIME))/60
)
select PARENT_SAMPLE_ID, count(*) THAW_COUNT
from SelectedSample
group by PARENT_SAMPLE_ID
But this will return a THAW_COUNT of 3 as sampleId:s 5 and 6 will be in different groups because the grouping is based on hour intervals from freeze time of sampleId 2 only. How do I get the grouping for group 2 to start from the first record outside the first group (sampleId 5) and so on?
This can be treated as a gaps and islands problem. Using some windows functions to check counts and using LAG to look at the "previous" row we can solve this. If you have multiple values for SAMPLE_ID you will want to add some partitioning.
create table #Something
(
SAMPLE_ID int
, FREEZE_TIME datetime
, PARENT_SAMPLE_ID int
)
insert #Something
select 1, null, null union all
select 2, '2015-11-27 10:23:10', 1 union all
select 3, '2015-11-27 10:59:23', 1 union all
select 4, '2015-11-27 11:05:43', 1 union all
select 5, '2015-11-27 12:53:48', 1 union all
select 6, '2015-11-27 13:42:25', 1;
with MyGroups as
(
select *
, GroupNum = count(IsNewGroup) over (order by FREEZE_TIME rows unbounded preceding)
from
(
select *
, IsNewGroup = case when LAG(FREEZE_TIME, 1, '') over(order by FREEZE_TIME) < dateadd(hour, -1, FREEZE_TIME) then 1 end
from #Something
) x
)
select coalesce(PARENT_SAMPLE_ID, SAMPLE_ID)
, count(distinct GroupNum)
from MyGroups
group by coalesce(PARENT_SAMPLE_ID, SAMPLE_ID)
drop table #Something

Selecting records for deletions based on relationship with previous and next records

I have a SQL Server 2014 table with millions of gps coordinates, each at a particular time. However the interval between the registrations is not fixed and varies from 1 second to a couple of hours. I only want to keep one measurement every 4 minutes, so the other records have to be deleted.
I tried a WHILE loop in T-SQL that traverses every record, with inside the loop a select statement with a double CROSS APPLY to only return a record if it sits in beween 2 other records which are not more than 4 minutes apart. However this strategy turns out to be too slow.
Can this be done with a set-based solution ? Or is there a way to speed-up this query ? (the test query below is just printing, not yet deleting)
SELECT * INTO #myTemp FROM gps ORDER BY TimePoint asc
declare #Id Uniqueidentifier
declare #d1 varchar(19)
declare #d2 varchar(19)
declare #d3 varchar(19)
While EXISTS (select * from #myTemp )
BEGIN
select top 1 #Id = ID FROM #myTemp order by TimePoint asc
SELECT
#d1 = convert(varchar(19), a.justbefore, 121),
#d2 = convert(varchar(19), b.tijdstip, 121),
#d3 = convert(varchar(19), c.justafter, 121)
FROM Gps B CROSS APPLY
(
SELECT top 1 TimePoint as justbefore
FROM Gps
WHERE (B.TimePoint > TimePoint ) AND (B.Id = #Id )
ORDER by TimePoint desc
) A
CROSS APPLY (
SELECT top 1 TimePoint as justafter
FROM Gps
WHERE (Datediff(n,A.justbefore,TimePoint ) between -4 AND 0)
AND (B.TimePoint < TimePoint )
ORDER by TimePoint asc
) C
print 'ID=' + Cast(#id as varchar(50))
+ ' / d1=' + #d1 + ' / d2=' + #d2 + ' / d3=' + #d3
DELETE #myTemp where Id = #id
END
--
Sample data:
Id TimePoint Lat Lon
1 20170725 13:05:27 12,256 24,123
2 20170725 13:10:27 12,254 24,120
3 20170725 13:10:29 12,253 24,125
4 20170725 13:11:55 12,259 24,127
5 20170725 13:11:59 12,255 24,123
6 20170725 13:14:28 12,254 24,126
7 20170725 13:16:52 12,259 24,121
8 20170725 13:20:53 12,257 24,125
In this case records 3,4,5 should be deleted.
Record 7 should stay as the gap between 7 and 8 is longer than 4 minutes.
Looking at the numbers... It looks like 1 & 2 stay (5 mins apart)...3, 4, & 5 should go... 6 stays (4 mins from 2)... 7 should go (only 2 mins from 6) and 8 stays (6 mins from 6)...
If this is correct, the following will do what you're looking for...
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
CREATE TABLE #TestData (
Id INT NOT NULL PRIMARY KEY CLUSTERED,
TimePoint DATETIME2(0) NOT NULL,
Lat DECIMAL(9,3),
Lon DECIMAL(9,3)
);
INSERT #TestData (Id, TimePoint, Lat, Lon) VALUES
(1, '20170725 13:05:27', 12.256, 24.123),
(2, '20170725 13:10:27', 12.254, 24.120),
(3, '20170725 13:10:29', 12.253, 24.125),
(4, '20170725 13:11:55', 12.259, 24.127),
(5, '20170725 13:11:59', 12.255, 24.123),
(6, '20170725 13:14:28', 12.254, 24.126),
(7, '20170725 13:16:52', 12.259, 24.121),
(8, '20170725 13:20:53', 12.257, 24.125);
-- SELECT * FROM #TestData td;
--================================================================================
WITH
cte_AddLag AS (
SELECT
td.Id, td.TimePoint, td.Lat, td.Lon,
MinFromPrev = DATEDIFF(mi, LAG(td.TimePoint, 1) OVER (ORDER BY td.TimePoint), td.TimePoint)
FROM
#TestData td
),
cte_TimeGroup AS (
SELECT
*,
TimeGroup = ISNULL(SUM(al.MinFromPrev) OVER (ORDER BY al.TimePoint ROWS UNBOUNDED PRECEDING) / 4, 0)
FROM
cte_AddLag al
)
SELECT TOP 1 WITH TIES
tg.Id,
tg.TimePoint,
tg.Lat,
tg.Lon
FROM
cte_TimeGroup tg
ORDER BY
ROW_NUMBER() OVER (PARTITION BY tg.TimeGroup ORDER BY tg.TimePoint);
Results...
Id TimePoint Lat Lon
----------- --------------------------- --------------------------------------- ---------------------------------------
1 2017-07-25 13:05:27 12.256 24.123
2 2017-07-25 13:10:27 12.254 24.120
6 2017-07-25 13:14:28 12.254 24.126
8 2017-07-25 13:20:53 12.257 24.125
HTH, Jason

Counting records in a subquery

I have a table with records Holding patrols of guards in SQL Server 2008R2.
Whenever a duty starts a new alert number is created and within this alert number there a patrols with a starting time.
Per 12 hours we can bill a flat rate when at least one patrol has been performed. When under the same alert number the 12 hour range is exceeded, a further flat rate has to be billed.
The calculation of the 12 hours starts with the time of the first patrol.
I tried with a temp table but could not solve it so far.
DECLARE #t1 TABLE (
AlertNo INT,
Starttime SMALLDATETIME,
Endtime SMALLDATETIME
)
INSERT INTO #t1 (AlertNo, Starttime, Endtime)
SELECT AlertNo,
Starttimepatrol,
DATEADD(HOUR, 12, Starttimepatrol)
FROM tblAllPatrols
WHERE PatrolNo = 1
SELECT AlertNo,
(
SELECT COUNT(*)
FROM [tblAllPatrols] a
INNER JOIN #t1 b ON b.AlertNo = a.AlertNo
WHERE a.Starttimepatrol BETWEEN b.Starttime AND b.Endtime
) AS patrols
FROM [vwAlleDatensaetze]
GROUP BY AlertNo
I know that this is not the end of the Story, but as I cannot even count the numbers of patrols I cannot find a way to solve the Problem.
It should somehow "group" the patrols over 12-hour ranges per alert number and then count how many groups exists under the same alert number.
Hope, someone of you can lead me to the result I Need.
Thanks your help
Michael
Try this, it assumes that after the first patrol the billing period is a multiple of 8 hours from this time:
SQL Fiddle
MS SQL Server 2008 Schema Setup:
Query 1:
DECLARE #Patrols TABLE
(
AlertNo INT IDENTITY PRIMARY KEY,
StartTime DateTime
)
INSERT INTO #Patrols (StartTime)
VALUES ('20160126 09:57'),
('20160126 10:21'),
('20160126 19:54'),
('20160126 23:21'),
('20160127 08:13'),
('20160127 16:43'),
('20160128 07:33')
;WITH FirstBillingPeriodCTE
AS
(
SELECT MIN(StartTime) as BillingStartTime,
DateAdd(HOUR, 12, MIN(StartTime)) As BillingEndTime,
1 As BillingPeriod
FROM #Patrols
),
Numbers
As
(
SELECT num
FROM (Values (0),(1), (2), (3), (4), (5), (6), (7), (8), (9)) AS n(Num)
),
BillingPeriodsCTE
AS
(
SELECT DATEADD(Hour, 8 * (BillingPeriod + Numbers.Num), BillingStartTime) AS BillingStartTime,
DATEADD(Hour, 8 * (BillingPeriod + Numbers.Num), BillingEndTime) AS BillingEndTime,
BillingPeriod + Numbers.Num As BillingPeriod
FROM FirstBillingPeriodCTE
CROSS JOIN Numbers
)
SELECT COUNT(DISTINCT BillingPeriod)
FROM #Patrols P
INNER JOIN BillingPeriodsCTE B
ON P.StartTime >= B.BillingStartTime AND P.StartTime < B.BillingEndTime
Results:
| |
|---|
| 4 |
Here is a query that will give each billing period, up to 65,535 billing periods, accurate to the second.
My solution uses a calculated "Tally" table, but you would be better off in the long run to create your own physical "Tally" table in your database. See What is the best way to create and populate a numbers table? for more details.
You should be able to replace #tblPatrols with your patrol table.
DECLARE #tblPatrols TABLE (alertNo int, startTime datetime);
DECLARE #hoursPerBillingPeriod int, #toHoursConversion float;
SET #hoursPerBillingPeriod = 12;
SET #toHoursConversion = 60 * 60;
INSERT INTO #tblPatrols (alertNo, startTime)
VALUES
(1, '2016-01-28 05:57')
, (1, '2016-01-28 07:23')
, (1, '2016-01-28 08:10')
, (2, '2016-01-28 09:05')
, (2, '2016-01-28 12:22')
, (2, '2016-01-28 16:06')
, (2, '2016-01-28 23:45')
, (2, '2016-01-29 00:05')
, (3, '2016-01-28 12:00')
, (3, '2016-01-28 16:06')
, (3, '2016-01-29 00:00')
, (4, '2016-01-28 12:00')
, (4, '2016-01-28 16:06')
, (4, '2016-01-28 23:59:59.997')
;
;WITH
--......................
--This section used to simulate a "Tally" table... you would be better off to Create a physical Tally table
-- see: https://stackoverflow.com/questions/1393951/what-is-the-best-way-to-create-and-populate-a-numbers-table
Pass0 as (select 1 as C union all select 1) --2 rows
, Pass1 as (select 1 as C from Pass0 as A, Pass0 as B) --4 rows
, Pass2 as (select 1 as C from Pass1 as A, Pass1 as B) --16 rows
, Pass3 as (select 1 as C from Pass2 as A, Pass2 as B) --256 rows
, Pass4 as (select 1 as C from Pass3 as A, Pass3 as B)--65536 rows
, Tally as (select row_number() over(order by C) - 1 as N from Pass4) --65536 rows
--........................
,cteNumBillings as (
SELECT fp.alertNo
, firstPatrolTime = min(fp.startTime)
, lastPatrolTime = max(fp.startTime)
, hoursBetweenStartMinMax = datediff(second, min(fp.startTime), max(fp.startTime)) / #toHoursConversion
, numberOfBillingPeriods = floor(((datediff(second, min(fp.startTime), max(fp.startTime)) / #toHoursConversion) / #hoursPerBillingPeriod) + 1)
FROM #tblPatrols fp
GROUP BY fp.alertNo
)
SELECT b.alertNo
--This is the "x" value of the expression "Billing Period x of y"
, BillingPeriodNumber = t.N + 1
, BillingPeriodPatrolCount =
(select count(*)
from #tblPatrols p
where p.alertNo = b.alertNo
and p.startTime >= dateadd(hour, 12 * t.N, b.firstPatrolTime)
and p.startTime < dateadd(hour, 12 * (t.N+1), b.firstPatrolTime)
)
, BillingStart = dateadd(hour, 12 * t.N, b.firstPatrolTime)
, BillingEnd = dateadd(second, -1, dateadd(hour, 12 * (t.N + 1), b.firstPatrolTime))
--This is the "y" value of the expression "Billing Period x of y"
, TotalBillingPeriodCount = b.numberOfBillingPeriods
FROM cteNumBillings b
INNER JOIN Tally t ON t.N >= 0 and t.N < b.numberOfBillingPeriods
ORDER BY 1,2
;
I found a solution by myself, which seems to be easier and I could not find any mistake using it.
I take the first Startime of the first patrol in a variable. Then I use datediff for die difference of the all StartTimePatrol to the startime of the first patrol and divide it by 12 hours
set #BillingPeriod=(select (datediff(hour,#StartTime,#StartTimePatrol)/12)+1)
then I put the result of each record in a temp table
insert into #t2 ( Alertno, Starttime, Billings )
values ( #Alertno, #StartTimePatrol, #BillingPeriod )
then I group the altertno and Billings and count them
select alertno, count(Billings ) from (select alertno, Billings from #t2
group by alertno, Billings ) temp group by alertno
The result looks correct for me.
Thanks for all replies.
Michael

how to display different data sequentially date of status 1 first in sql server

how to display different data sequentially date of status 1 first
result like this image
First creating the test data:
DECLARE #mydate TABLE
(
date_time DATETIME,
statusid int
)
INSERT INTO #mydate
( date_time, statusid )
VALUES
('02/25/2015 12:09:00', 0),
('02/25/2015 12:10:00', 0),
('02/25/2015 12:11:00', 0),
('02/25/2015 12:12:00', 1),
('02/25/2015 12:13:00', 1),
('02/25/2015 12:14:00', 0),
('02/25/2015 12:15:00', 0),
('02/25/2015 12:16:00', 1),
('02/25/2015 12:17:00', 1),
('02/25/2015 12:18:00', 1),
('02/25/2015 12:19:00', 1),
('02/25/2015 12:20:00', 0),
('02/25/2015 12:21:00', 0);
Lets find out what status is before each record
; WITH StatusRecs AS
(
SELECT
m.date_time,
m.statusid,
LAG(m.statusid) OVER (ORDER BY date_time) AS prev_status
FROM #mydate m
)
Now going to pull all of the status 1 records that are different from the prev status to find the beginning of each set, we are also going to pull the date of the next status = 1 record in next_start_date
,StartStatus AS
(
SELECT
s.date_time,
s.statusid,
LEAD(s.date_time) OVER (ORDER BY s.date_time) AS next_start_date
FROM StatusRecs s
WHERE s.statusid != ISNULL(s.prev_status, -1)
AND s.statusid = 1
)
Now lets pull it all together to get the last 0 status record before the next status 1 record
,MyRecs AS
(
SELECT * FROM StartStatus ss
OUTER APPLY
(
SELECT TOP 1 sr.date_time AS date_time2, sr.statusid AS statusid2
FROM StatusRecs sr
WHERE sr.date_time > ss.date_time
AND (sr.date_time < ss.next_start_date OR ss.next_start_date IS NULL)
ORDER BY sr.date_time DESC
) m
)
Now we format and output the table
SELECT m.date_time, m.statusid, m.date_time2, m.statusid2, DATEDIFF(MINUTE, m.date_time, m.date_time2) AS duration FROM MyRecs m
date_time statusid date_time2 statusid2 duration
2015-02-25 12:12:00.000 1 2015-02-25 12:15:00.000 0 3
2015-02-25 12:16:00.000 1 2015-02-25 12:21:00.000 0 5
this is an ugly example due to all the table read on the CTE but it might help in some way.. didn't have a SQLfiddle to test it out... try set one up for your questions, always helps.
with myTableNumbered (myOrder,date_time,status) AS (
select
ROW_NUMBER() OVER (ORDER BY date_time) myOrder,
date_time,
status
from myTable
)
select
m.date_time,
m.status,
next.date_time datetime_2,
next.status status2,
CONVERT(VARCHAR(10),datediff(minute,m.date_time,next.date_time))+'minut'duration
from myTableNumbered m
OUTER APPLY (
select TOP 1
next.date_time,
next.status
from myTableNumbered next
where
next.myOrder > m.myOrder and
next.status = 0 and
ISNULL((select status from myTableNumbered prev where prev.myOrder-1 = next.myOrder),1) = 1
order by next.date_time asc
) next
where
m.status = 1 and
ISNULL((select status from myTableNumbered prev where prev.myOrder+1 = m.myOrder),0) = 0

Trouble creating SQL Query to update line item amounts

I'm going to preface this question with the disclaimer that creating what I call "complex" queries isn't in my forte. Most of the time, there is a much simpler way to accomplish what I'm trying to accomplish so if the below query isn't up to par, I apologize.
With that said, I have a table that keeps track of Vendor Invoices and Vendor Invoice Items (along with a Vendor Invoice Type and Vendor Invoice Item Type). Our bookkeeper wants a report that simply shows: Vendor | Location | Inv Number | Inv Type | Item Type | Inv Date | Rental Fee | Restock Fee | Shipping Fee | Line Item Cost | Total (Line Item + Fees)
Most of the time, one vendor invoice is one line. However, there are exceptions where a vendor invoice can have many item types, thus creating two rows. Not a big deal EXCEPT the fees (Rental, Restock, Shipping) are attached to the Vendor Invoice table. So, I first created a query that checks the temp table for Invoices that have multiple rows, takes the last row, and zero's out the fees. So that only one line item would have the fee. However, our bookkeeper doesn't like that. Instead, she'd like the fees to be "distributed" among the line items.
So, if a vendor invoice has a $25 shipping charge, has two line items, then each line item would be $12.50.
After working with the query, I got it to update the Last Row to be the adjusted amount but row 1+ would have the original amount.
I'm going to post my entire query here (again - I'm sorry that this may not be the best looking query; however, suggestions are always welcome)
DROP TABLE #tVendorInvoiceReport
DROP TABLE #tSummary
SELECT v.Name AS Vendor ,
vii.Location ,
vi.VendorInvNumber ,
vit.Descr AS InvoiceType ,
vii.VendorInvoiceItemType ,
CONVERT(VARCHAR(10), vi.VendorInvDate, 120) VendorInvDate ,
vi.RentalFee ,
vi.RestockFee ,
vi.ShippingFee ,
SUM(vii.TotalUnitCost) TotalItemCost ,
CONVERT(MONEY, 0) TotalInvoice ,
RowID = IDENTITY( INT,1,1)
INTO #tVendorInvoiceReport
FROM dbo.vVendorInvoiceItems AS vii
JOIN dbo.VendorInvoices AS vi ON vii.VendorInvID = vi.VendorInvID
JOIN dbo.Vendors AS v ON vi.VendorID = v.VendorID
JOIN dbo.VendorInvoiceTypes AS vit ON vi.VendorInvTypeID = vit.VendorInvTypeID
WHERE vi.VendorInvDate >= '2012-01-01'
AND vi.VendorInvDate <= '2012-01-31'
GROUP BY v.Name ,
vii.Location ,
vi.VendorInvNumber ,
vit.Descr ,
vii.VendorInvoiceItemType ,
CONVERT(VARCHAR(10), vi.VendorInvDate, 120) ,
vi.RentalFee ,
vi.RestockFee ,
vi.ShippingFee
ORDER BY v.Name ,
vii.Location ,
vi.VendorInvNumber ,
vit.Descr ,
vii.VendorInvoiceItemType ,
CONVERT(VARCHAR(10), vi.VendorInvDate, 120)
SELECT VendorInvNumber ,
COUNT(RowID) TotalLines ,
MAX(RowID) LastLine
INTO #tSummary
FROM #tVendorInvoiceReport
GROUP BY VendorInvNumber
WHILE ( SELECT COUNT(LastLine)
FROM #tSummary AS ts
WHERE TotalLines > 1
) > 0
BEGIN
DECLARE #LastLine INT
DECLARE #NumItems INT
SET #LastLine = ( SELECT MAX(LastLine)
FROM #tSummary AS ts
WHERE TotalLines > 1
)
SET #NumItems = ( SELECT COUNT(VendorInvNumber)
FROM #tVendorInvoiceReport
WHERE VendorInvNumber IN (
SELECT VendorInvNumber
FROM #tSummary
WHERE LastLine = #LastLine )
)
UPDATE #tVendorInvoiceReport
SET RentalFee = ( RentalFee / #NumItems ) ,
RestockFee = ( RestockFee / #NumItems ) ,
ShippingFee = ( ShippingFee / #NumItems )
WHERE RowID = #LastLine
DELETE FROM #tSummary
WHERE LastLine = #LastLine
--PRINT #NumItems
END
UPDATE #tVendorInvoiceReport
SET TotalInvoice = ( TotalItemCost + RentalFee + RestockFee + ShippingFee )
SELECT Vendor ,
Location ,
VendorInvNumber ,
InvoiceType ,
VendorInvoiceItemType ,
VendorInvDate ,
RentalFee ,
RestockFee ,
ShippingFee ,
TotalItemCost ,
TotalInvoice
FROM #tVendorInvoiceReport AS tvir
I sincerely appreciate anyone who took the time to read this and attempt to point me in the right direction.
Thank you,
Andrew
PS - I did try and remove "WHERE RowID = #LastLine" from the first Update, but that changed the Shipping Fees for the first line with two items to "0.0868" instead of 12.50 ($25/2)
If I understand correctly, you're looking for a way to split something like an invoice shipping fee over one or more invoice items.
I created some sample invoice and invoice item tables shown below and used the
over(partition) clause to split out the shipping per item.
-- sample tables
declare #Invoice table (InvoiceID int, customerID int, Date datetime, ShippingFee float)
declare #InvoiceItem table (InvoiceItemID int identity, InvoiceID int, ItemDesc varchar(50), Quantity float, ItemPrice float)
-- Example 1
insert #Invoice values(1, 800, getdate(), 20);
insert #InvoiceItem values(1, 'Widget', 1, 10.00)
insert #InvoiceItem values(1, 'Wing Nut', 5, 2.00)
insert #InvoiceItem values(1, 'Doodad', 8, 0.50)
insert #InvoiceItem values(1, 'Thingy', 3, 1.00)
-- Example 2
insert #Invoice values(2, 815, getdate(), 15);
insert #InvoiceItem values(2, 'Green Stuff', 10, 1.00)
insert #InvoiceItem values(2, 'Blue Stuff', 10, 1.60)
-- Example 3
insert #Invoice values(3, 789, getdate(), 15);
insert #InvoiceItem values(3, 'Widget', 10, 1.60)
-- query
select
n.InvoiceID,
n.InvoiceItemID,
n.ItemDesc,
n.Quantity,
n.ItemPrice,
ExtendedPrice = n.Quantity * n.ItemPrice,
Shipping = i.ShippingFee / count(n.InvoiceItemID) over(partition by n.InvoiceID)
from #InvoiceItem n
join #Invoice i on i.InvoiceID = n.InvoiceID
Output:
InvoiceID InvoiceItemID ItemDesc Quantity ItemPrice ExtendedPrice Shipping
1 1 Widget 1 10 10 5
1 2 Wing Nut 5 2 10 5
1 3 Doodad 8 0.5 4 5
1 4 Thingy 3 1 3 5
2 5 Green Stuff 10 1 10 7.5
2 6 Blue Stuff 10 1.6 16 7.5
3 7 Widget 10 1.6 16 15

Resources