I'm having a lot of difficulty trying to create a view that flattens the data without nulls. I've supplied the code that creates two basic tables and my view code so you can see what I've tried so far. Please note that the two tables do not have a matching primary or foreign key column, so the summary in the view is created by just joining on City. I can't use XML because my team of data analysts all have intermediate skills and won't be able to understand it. I considered using a recursive CTE, but I can't get it right. The result produces 6 lines but I want 3 lines.
Thanks for any ideas about a better way to achieve this.
CREATE TABLE A (
OrdID int,
Cat varchar(255),
Qty int,
City varchar(255),
Ctry varchar(255)
);
INSERT INTO A (OrdID, Cat, Qty, City, Ctry)
VALUES (1, 'TV', 5,'London', 'England');
INSERT INTO A (OrdID, Cat, Qty, City, Ctry)
VALUES (2, 'Laptop', 3,'London', 'England');
INSERT INTO A (OrdID, Cat, Qty, City, Ctry)
VALUES (3, 'Laptop', 4, 'Berlin', 'Germany');
CREATE TABLE Cust (
CustID int,
CustType varchar(255),
City varchar(255),
NumItems int,
);
INSERT INTO Cust (CustID, CustType, City, NumItems)
VALUES (1, 'New', 'London', 2);
INSERT INTO Cust (CustID, CustType, City, NumItems)
VALUES (2, 'Returning','London', 5);
INSERT INTO Cust (CustID, CustType, City, NumItems)
VALUES (3, 'Returning','Berlin', 2);
INSERT INTO Cust (CustID, CustType, City, NumItems)
VALUES (4, 'New','Berlin', 8);
alter view My_View
as
With CTE_FlattenNulls
as
(
Select
S.Cat
, S.Qty
, S.City
, S.Ctry
, case when C.CustType like 'New' then sum(C.NumItems) end as NewC
, case when C.CustType like 'Returning' then sum(C.NumItems) end as RetC
from A as S
left join Cust as C
on S.City = C.City
group by
S.Cat
, S.Qty
, S.City
, S.Ctry
, C.CustType
)
select
Cat
,Qty
,City
,Ctry
,NewC
,RetC
,SUM(IsNull(NewC, 0) + IsNull(RetC, 0)) as TotC
from CTE_FlattenNulls
group by
Cat
,Qty
,City
,Ctry
,NewC
,RetC
go
Just adding the output that I wanted:
Cat
Qty
City
Cntry
NewC
RetCust
TotC
Laptop
4
Berlin
Germany
8
2
10
Laptop
3
London
England
2
5
7
TV
5
London
England
2
5
7
You were very close.
See comments in code for explanation.
With CTE_FlattenNulls
as
(
Select S.Cat, S.Qty, S.City, S.Ctry,
-- To do conditional summation case expression needs to be inside the SUM function
sum( case when C.CustType like 'New' then C.NumItems else 0 end ) as NewC,
sum( case when C.CustType like 'Returning' then C.NumItems else 0 end ) as RetC
from A as S
left join Cust as C
on S.City = C.City
group by
S.Cat
, S.Qty
, S.City
, S.Ctry
-- then you do not need to group by this column and therefore you do not get extra rows
--, C.CustType
)
select Cat, Qty, City, Ctry, NewC, RetC,
-- As per your example, you would no longer need GROUP BY,
-- therefore SUM function should be removed
SUM(IsNull(NewC, 0) + IsNull(RetC, 0)) as TotC
from CTE_FlattenNulls
-- As per your example, you would no longer need GROUP BY
group by Cat, Qty, City, Ctry
-- ,NewC -- Definitely not needed anymore
-- ,RetC -- Definitely not needed anymore
Everything else stays the same
To get to your result, why can you not just do a simple group by with conditional sum ?
It has no need for a CTE
See this example, also in this DBFiddle
select A.Cat,
A.Qty,
A.City,
min(A.Ctry) as Country,
sum(case when C.CustType = 'New' then C.NumItems else 0 end) as NewC,
sum(case when C.CustType = 'Returning' then C.NumItems else 0 end) as RetCust,
sum(C.NumItems) as TotC
from A
join Cust C on A.City = C.City
group by A.Cat,
A.Qty,
A.City
order by A.Cat, A.City
it returns this
Cat
Qty
City
Country
NewC
RetCust
TotC
Laptop
4
Berlin
Germany
8
2
10
Laptop
3
London
England
2
5
7
TV
5
London
England
2
5
7
Related
I have a table that shows the chain of custody of a piece of evidence as it is being analyzed. The evidence may just be analyzed at one location, but can also be transferred to different labs to be analyzed. I am trying to write a query that returns the Case Number if it is analyzed in different labs.
Here is an example of the data: CaseNumber 1 starts off in the Chemistry Lab, then transferred to DNA lab, then transferred back to Chemistry Lab. Case 2 is just associated in the Chem lab (do not need to see this case in the query results).
ID CaseNumber Lab ActionDate Action
-------------------------------------------------------------------
1 1 Chem 1/1/2019 case created
2 1 Chem 1/2/2019 container created
3 1 DNA 2/1/2019 container routed to DNA
4 1 DNA 2/3/2019 evidence analyzed
5 1 Chem 2/3/2019 edit route
6 2 Chem 2/4/2019 create case
7 2 Chem 2/5/2019 analyze evidence
Here is what I have so far. This returns casenumber and unique lab but I would like to incorporate the ActionDate somehow to show the actual date it was routed to the new location.
SELECT DISTINCT
casenumber, lab
FROM
summary
WHERE
casenumber IN (SELECT a.casenumber
FROM summary a
JOIN summary b on b.casenumber = a.casenumber AND b.lab <> a.lab)
AND actiondate BETWEEN '2019-01-02' AND '2019-01-04'
ORDER BY
casenumber
I expect the results of the query to look like the following. I would like to see the first entry per Lab (since that is the date it was actually routed to the new location)
ID CaseNumber Lab ActionDate Action
-------------------------------------------------------------------
1 1 Chem 1/1/2019 case created
3 1 DNA 2/1/2019 container routed to DNA
5 1 Chem 2/3/2019 container routed to Chem
There are plenty of ways to accomplish this, here is one of them. Note this will only work on sql server 2012+.
create table #Something
(
ID int
, CaseNumber int
, Lab varchar(10)
, ActionDate date
, Action varchar(50)
)
insert #Something values
(1, 1, 'Chem', '1/1/2019', 'case created')
, (2, 1, 'Chem', '1/2/2019', 'container created')
, (3, 1, 'DNA', '2/1/2019', 'container routed to DNA')
, (4, 1, 'DNA', '2/3/2019', 'evidence analyzed')
, (5, 1, 'Chem', '2/3/2019', 'edit route')
, (6, 2, 'Chem', '2/4/2019', 'create case')
, (7, 2, 'Chem', '2/5/2019', 'analyze evidence')
;
with SortedResults as
(
select s.ID
, s.CaseNumber
, s.Lab
, s.ActionDate
, Action = case when LAG(Lab, 1) over(partition by CaseNumber order by ActionDate) is null then Action
when LAG(Lab, 1) over(partition by CaseNumber order by ActionDate) = Lab then NULL
else 'container routed to ' + Lab end
from #Something s
)
select *
from SortedResults
where Action > ''
order by CaseNumber, ActionDate
drop table #Something
I have 2 tables name EmployeeInfo and Leave and I am storing the values that which employee have taken which type of leave in month and how many times.
I am trying to calculate the number of leaves of same type but I'm stuck at one point for long time.
IF EXISTS(SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('Leave'))
BEGIN;
DROP TABLE [Leave];
END;
GO
IF EXISTS(SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('EmployeeInfo'))
BEGIN;
DROP TABLE [EmployeeInfo];
END;
GO
CREATE TABLE [EmployeeInfo] (
[EmpID] INT NOT NULL PRIMARY KEY,
[EmployeeName] VARCHAR(255)
);
CREATE TABLE [Leave] (
[LeaveID] INT NOT NULL PRIMARY KEY,
[LeaveType] VARCHAR(255) NULL,
[DateFrom] VARCHAR(255),
[DateTo] VARCHAR(255),
[Approved] Binary,
[EmpID] INT FOREIGN KEY REFERENCES EmployeeInfo(EmpID)
);
GO
INSERT INTO EmployeeInfo([EmpID], [EmployeeName]) VALUES
(1, 'Marcia'),
(2, 'Lacey'),
(3, 'Fay'),
(4, 'Mohammad'),
(5, 'Mike')
INSERT INTO Leave([LeaveID],[LeaveType],[DateFrom],[DateTo], [Approved], [EmpID]) VALUES
(1, 'Annual Leave','2018-01-08 04:52:03','2018-01-10 20:30:53', 1, 1),
(2, 'Sick Leave','2018-02-10 03:34:41','2018-02-14 04:52:14', 0, 2),
(3, 'Casual Leave','2018-01-04 11:06:18','2018-01-05 04:11:00', 1, 3),
(4, 'Annual Leave','2018-01-17 17:09:34','2018-01-21 14:30:44', 0, 4),
(5, 'Casual Leave','2018-01-09 23:31:16','2018-01-12 15:11:17', 1, 3),
(6, 'Annual Leave','2018-02-16 18:01:03','2018-02-19 17:16:04', 1, 2)
My query which I have tried so far look something like this.
SELECT Info.EmployeeName, Leave.LeaveType, SUM(DATEDIFF(Day, Leave.DateFrom, Leave.DateTo)) [#OfLeaves], DatePart(MONTH, Leave.DateFrom)
FROM EmployeeInfo Info, Leave
WHERE Info.EmpID = Leave.EmpID AND Approved = 1
GROUP BY Info.EmployeeName, Leave.LeaveType, [Leave].[DateFrom], [Leave].[DateTo]
And the record like given below
EmployeeName LeaveType #OfLeaves MonthNumber
-------------- ----------------- ----------- -----------
Fay Casual Leave 1 1
Fay Casual Leave 3 1
Lacey Annual Leave 3 2
Marcia Annual Leave 2 1
I want the record to look like this
EmployeeName LeaveType #OfLeaves MonthNumber
-------------- ----------------- ----------- -----------
Fay Casual Leave 4 1
Lacey Annual Leave 3 2
Marcia Annual Leave 2 1
If you don't want to modify existing query due to some constraint, this might work:
Select iq.EmployeeName, iq.LeaveType, SUM(iq.#OfLeaves) as #OfLeaves, iq.MonthNumber
From (
SELECT Info.EmployeeName, Leave.LeaveType, SUM(DATEDIFF(Day, Leave.DateFrom, Leave.DateTo)) [#OfLeaves], DatePart(MONTH, Leave.DateFrom) as MonthNumber
FROM EmployeeInfo Info, Leave
WHERE Info.EmpID = Leave.EmpID AND Approved = 1
GROUP BY Info.EmployeeName, Leave.LeaveType, [Leave].[DateFrom], [Leave].[DateTo]
)iq
group by iq.EmployeeName, iq.LeaveType, iq.MonthNumber
This just need small adjustment with your query in the GROUP BY clause. Instead of grouping them by [Leave].[DateFrom] and [Leave].[DateTo] which causes the row to be separated, you need to group it with the calculated column that uses datepart.
SELECT Info.EmployeeName,
Leave.LeaveType,
SUM(DATEDIFF(Day, Leave.DateFrom, Leave.DateTo)) [#OfLeaves],
DatePart(MONTH, Leave.DateFrom)
FROM EmployeeInfo Info
INNER JOIN Leave
ON Info.EmpID = Leave.EmpID
WHERE Approved = 1
GROUP BY Info.EmployeeName,
Leave.LeaveType,
DatePart(MONTH, Leave.DateFrom) -- <<<< change only this part
Here's a Demo.
I have also modified the syntax into ANSI format.
I am facing this problem where I need to compare the most recent row with the immediate previous one based on the same criteria (it will be trader in this case).
Here is my table:
ID Trader Price
-----------------
1 abc 5
2 xyz 5.2
3 abc 5.7
4 xyz 5
5 abc 5.2
6 abc 6
Here is the script
CREATE TABLE Sale
(
ID int not null PRIMARY KEY ,
trader varchar(10) NOT NULL,
price decimal(2,1),
)
INSERT INTO Sale (ID,trader, price)
VALUES (1, 'abc', 5), (2, 'xyz', 5.2),
(3, 'abc', 5.7), (4, 'xyz', 5),
(5, 'abc', 5.2), (6, 'abc', 6);
So far I am working with this solution that is not perfect yet
select
a.trader,
(a.price - b.price ) New_price
from
sale a
join
sale b on a.trader = b.trader and a.id > b.ID
left outer join
sale c on a.trader = c.trader and a.id > c.ID and b.id < c.ID
where
c.ID is null
Above is not perfect because I want to compare only the most recent with the immediate previous on... In this sample for example
Trader abc : I will compare only id = 6 and id = 5
Trader xyz : id = 4 and id = 2
Thanks for any help!
If you are using SQL Server 2012 or later, you can use functions LEAD and LAG to join previous and next data. Unfortunately these function can only be used in SELECT or ORDER BY clause, so you will need to use subquery to get the data you need:
SELECT t.trader, t.current_price - t.previous_price as difference
FROM (
SELECT
a.trader,
a.price as current_price,
LAG(a.price) OVER(PARTITION BY a.trader ORDER BY a.ID) as previous_price,
LEAD(a.price) OVER(PARTITION BY a.trader ORDER BY a.ID) as next_price
FROM sale a
) t
WHERE t.next_price IS NULL
Here in your subquery you create additional columns for previous and next value. Then in your main query you filter only these rows where next price is NULL - that indicates this is the last row for the specific trader.
I have seen few similar questions asked on Stackoverflow with same subject, However this is not fitting into any of those.
I have 3 Tables Purchase, Hotel, Car Purchase to Hotel and Car is 1 to 0 or many relationship. MS SQL 2008 Server.
Purchase Table
pid bookingdate ...
1
2
3
Hotel Table
hid pid amount rooms location brand...
1 1
2 1
3 1
4 3
4 3
Car Table
cid pid make model ...
1 1
2 2
3 2
What I want is display the hotel data in columns
pid bookingdate cid make model hid1 amount1 rooms1 location1 brand1 hid2 amount2 rooms2 location2 brand2 hid3 amount3 rooms3 location3 brand3 hid4 amount4 rooms4 location4 brand4
If there is only one hotel for given purchase id, other columns should be null. If more than 4 hotel ignore the other hotels(5th 6th ect)
Please assume Car table can have 0 or 1 at this stage.
(Advance::>> If Car table can 0 to many (relationship to Purchase) 1.) take only first record to account 2.) get the sum of records.)
Purpose of this project is generate a csv file which can upload to a some third party product. Currently we have about 100 columns for Purchase and Car about 30 Columns for Hotel (130 all). With Hotel rows displaying in this way it will be 220 (100 + 30 x 4) columns. And about 100K rows. Performance is an issue as well.
I tried few things, but nothing successful not even close to getting stuck. This was the closest I got but I have 30 columns to duplicate not just a one, In-fact I have a feeling that PIVOT is not the way to go. I'm thinking using DENSE_RANK() or something like that
SELECT pid
, hid
, DENSE_RANK() OVER(ORDER BY pid)
FROM HOTEL
WHERE pid IN (
SELECT pid
FROM Hotel
WHERE pid IN (
SELECT pid
FROM Purchase
WHERE rdate BETWEEN '2014-04-01' AND '2014-12-31'
)
GROUP BY pid
HAVING COUNT(pid) > 1
)
Assuming then we might able to process hotels with more than 1 attached to Purchase first, using a cursor (ugly) or something like that
Okay I have found a solution. You are welcome to refine and add your thoughts. Dynamically name columns would be handy. (ie product1,product2,product3,product4)
Schema
create table Purchase
(
pid int,
purchasedate date,
currency varchar(10),
paymenttype varchar(10),
creditcard varchar(10),
creditcardtype varchar(10)
);
create table Hotel
(
hid int,
pid int,
product varchar(30),
country varchar(10),
city varchar(10),
rooms int,
starrating int
);
create table Car
(
cid int,
pid int,
product varchar(30),
country varchar(10),
city varchar(10),
cancel int,
starrating int
);
insert into Purchase values (1, '2015-01-15','AUD', 'CC','12345678','AMEX')
insert into Purchase values (2, '2015-01-15','AUD', 'CC','12345678','AMEX')
insert into Purchase values (3, '2015-01-15','AUD', 'CC','12345678','AMEX')
insert into Purchase values (4, '2015-01-15','AUD', 'CC','12345678','AMEX')
insert into Purchase values (5, '2015-01-15','AUD', 'CC','12345678','AMEX')
insert into Hotel values (1,1, 'Five for Two','Australia', 'Melbourne','1','3')
insert into Hotel values (2,1, 'Five for None','Australia', 'Sydney','1','3')
insert into Hotel values (3,1, 'Five for Five','Australia', 'Melbourne','1','3')
insert into Hotel values (4,1, 'Five for Two','Australia', 'Jamboora','1','3')
insert into Hotel values (5,2, 'Five for Three','Australia', 'Sydney','1','3')
insert into Hotel values (6,2, 'Five for Love','Australia', 'Cook','1','3')
insert into Hotel values (7,2, 'Five for Grease','Australia', 'Darwin','1','3')
insert into Hotel values (8,3, 'Love Me','Australia', 'Darwin','1','3')
insert into Hotel values (9,4, 'Live for Grease','Australia', 'Footscray','1','3')
insert into Hotel values (10,4, 'Love Grease','Australia', 'Officer','1','3')
insert into Car values (1,1, 'Love Grease','Australia', 'Officer','1','3')
insert into Car values (2,2, 'Love Grease','Australia', 'Cook','1','3')
insert into Car values (3,4, 'Live Grease','Australia', 'Jamboora','1','3')
-- For Advance insert into Car values (4,4, 'Cove Grease','Australia', 'Melbourne','1','3')
And the Code is
SELECT *, DENSE_RANK() OVER(PARTITION BY pid ORDER BY hid DESC) AS Ranking
INTO #TempHotel
FROM Hotel
SELECT P.pid, P.purchasedate, P.currency, P.paymenttype, P.creditcard, P.creditcardtype
,C.cid, C.product, C.country, C.city, C.cancel, C.starrating
,H1.hid, H1.product, H1.country, H1.city,H1.rooms,H1.starrating
,H2.hid, H2.product, H2.country, H2.city,H2.rooms,H2.starrating
,H3.hid, H3.product, H3.country, H3.city,H3.rooms,H3.starrating
,H4.hid, H4.product, H4.country, H4.city,H4.rooms,H4.starrating
FROM Purchase P
left join Car C on P.pid = C.pid
left join #TempHotel H1 on P.pid = H1.pid and H1.Ranking = 1
left join #TempHotel H2 on P.pid = H2.pid and H2.Ranking = 2
left join #TempHotel H3 on P.pid = H3.pid and H3.Ranking = 3
left join #TempHotel H4 on P.pid = H4.pid and H4.Ranking = 4
DROP Table #TempHotel
You can also find this in SQL fiddle Here.
If the column count never changes you could use Pivot / Unpivot
Here is a technet article which says it all: http://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx
and a sample from the article.
-- Pivot table with one row and five columns
SELECT 'AverageCost' AS Cost_Sorted_By_Production_Days,
[0], [1], [2], [3], [4]
FROM
(SELECT DaysToManufacture, StandardCost
FROM Production.Product) AS SourceTable
PIVOT
(
AVG(StandardCost)
FOR DaysToManufacture IN ([0], [1], [2], [3], [4])
) AS PivotTable;
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