I have a table that contains order details. How can I create a row at the end that totals all my subtotals?
SELECT
o.order_id, o.itemDescription as Description,
o.quantity_shipped as [Quantity],
o.itemCost as [Price each],
(o.quantity_shipped * CAST(o.itemCost as float)) as [Sub Total]
FROM
dbo.order_items o
This will give you total by Order Id
SELECT o.order_id, SUM((o.quantity_shipped * CAST (o.itemCost as float))) as [TotalByOrderId]
FROM dbo.order_items o
GROUP BY o.order_id
This will give you grand total
SELECT SUM((o.quantity_shipped * CAST (o.itemCost as float))) as [GrandTotal]
FROM dbo.order_items o
A way (not the most performance-wise) can be the following:
;WITH CTE AS (
SELECT o.order_id, o.itemDescription as Description,
o.quantity_shipped as [Quantity],
o.itemCost as [Price each],
(o.quantity_shipped * CAST(o.itemCost as float)) as [Sub Total]
FROM dbo.order_items o)
SELECT *
FROM CTE
UNION ALL
SELECT NULL, 'Grand Total', NULL, NULL, SUM([Sub Total])
FROM CTE
Why would you do that? Adding a meaningless total row makes processing a lot more complicated later. Unless you do hierarchical sumbtotals for sub-lines.
The normal way this is handled by having totals as part of the invoice table.
Related
I have two table with columns as mentioned below:
CUSTOMER table as DIM_CUSTOMER:
ID_CUSTOMER, CUSTOMER_NAME
TRANSACTION table as FACT_TRANSACTION:
ID_CUSTOMER, DATE, TOTAL_PRICE, QUANTITY
Problem statement is to
Find top 100 customers and their average spend, average quantity by each year. Also find the percentage of change in their spend.
My approach:
SELECT TOP 100
YEAR(FT.DATE) AS [YEAR],
FT.ID_CUSTOMER AS [CUSTOMER NAME],
FT.TOTAL_PRICE AS [TOTAL AMT],
AVG(FT.TOTAL_PRICE) AS [AVG SPEND],
AVG(FT.QUANTITY) AS [AVG QUANTITY]
FROM
FACT_TRANSACTIONS FT
INNER JOIN
DIM_CUSTOMER DC ON FT.ID_CUSTOMER = DC.ID_CUSTOMER
GROUP BY
FT.DATE, FT.ID_CUSTOMER, FT.TOTAL_PRICE
ORDER BY
3 DESC
This is resulting in the top 100 customers based on their usage.
Now I need to determine the percentage change in their spend YEAR wise.
How can I do that? Probably using PIVOT option herein will help, but I'm unsure.
You can try using LAG in order to access the previous [AVG SPEND] for the current row. The idea is to group the data for each [CUSTOMER NAME] using PARTITION BY and then to order the data by the [YEAR]. The function will give us the previous result and we can calculated easily the difference.
Try something like this:
SELECT TOP 100
YEAR(FT.DATE) AS [YEAR],
FT.ID_CUSTOMER AS [CUSTOMER NAME],
FT.TOTAL_PRICE AS [TOTAL AMT],
AVG(FT.TOTAL_PRICE) AS [AVG SPEND],
AVG(FT.QUANTITY) AS [AVG QUANTITY]
INTO #DataSource
FROM
FACT_TRANSACTIONS FT
INNER JOIN
DIM_CUSTOMER DC ON FT.ID_CUSTOMER = DC.ID_CUSTOMER
GROUP BY
YEAR(FT.DATE), FT.ID_CUSTOMER, FT.TOTAL_PRICE
ORDER BY
[AVG SPEND] DESC
SELECT *
,[AVG SPEND] - LAG([AVG SPEND], 1, 0) OVER (PARTITION BY [CUSTOMER NAME] ORDER BY [YEAR])
FROM #DataSource
Note, that:
the function requires SQL Server 2012+
you can change the partitioning and ordering as you like in order to satisfy your real goal (for example you can use ORDER BY [YEAR] DESC
you can use the LEAD function in order to access the next value within the group if you want to calculated difference in advace
I materialized the data in temporary table, but you can use table variable or whatever you are using
Can you please try following SQL CTE query ?
;with topcustomers as(
SELECT distinct top 100
ID_CUSTOMER,
SUM(TOTAL_PRICE) over (partition by ID_CUSTOMER) as TotalSPEND
FROM FACT_TRANSACTION
order by TotalSPEND desc
), cte as (
SELECT
distinct
t.ID_CUSTOMER, YEAR(t.DATE) [YEAR], TotalSPEND,
AVG(t.QUANTITY * 1.0) over (partition by t.ID_CUSTOMER, YEAR(t.DATE)) as AverageQUANTITY,
AVG(t.TOTAL_PRICE * 1.0) over (partition by t.ID_CUSTOMER, YEAR(t.DATE)) as AverageSPEND
FROM FACT_TRANSACTION t
INNER JOIN topcustomers c on c.ID_CUSTOMER = t.ID_CUSTOMER
)
select
*,
( AverageSPEND - lag(AverageSPEND,1) over (partition by ID_CUSTOMER order by [YEAR]) ) * 100.0 / AverageSPEND as [%Change]
from cte
`Suppose I have a set of data with 2 fields - Type and Date. I am interested in finding (if exists) the the max common date across the various types. Is this easier to do in SQL or LINQ?
Given the data below the result should be 2018-02-01 as this is the max common date for all types. It there is no such date then no data is returned.
Type, Date
---------
1,2018-03-01
1,2018-02-01
1,2018-01-01
2,2018-02-01
2,2018-05-01
2,2018-01-01
3,2018-01-01
3,2018-03-01
3,2018-02-01
You could use:
SELECT TOP 1 [Date], COUNT(*) OVER(PARTITION BY Date) AS cnt
FROM tab
ORDER BY cnt DESC, [Date] DESC
DBFiddle Demo
This'll work if you have an unlimited or indeterminable number of Types:
CREATE TABLE #Sample ([Type] int, [DAte] date);
INSERT INTO #Sample
VALUES
(1,'20180301'),
(1,'20180201'),
(1,'20180101'),
(2,'20180201'),
(2,'20180501'),
(2,'20180101'),
(3,'20180101'),
(3,'20180301'),
(3,'20180201');
GO
WITH EntryCount AS(
SELECT [Type], [Date],
COUNT(*) OVER (PARTITION By [Date]) AS Entries
FROM #Sample)
SELECT MAX(Date)
FROM EntryCount EC
WHERE Ec.Entries = (SELECT COUNT(DISTINCT sq.[Type]) FROM #Sample sq);
GO
DROP TABLE #Sample;
Not sure how quick it'll be either though.
Example
Select Top 1 [Date]
from YourTable
Group By [Date]
Order By count([Type]) desc,[Date] desc
Returns
2018-02-01
This is not going to be very efficient not matter how you slice it because you have to compare across three groups. Assuming you have 3 types you could use a self join. Something like this.
select MAX(YourDate)
from YourTable yt
join YourTable yt2 on yt2.YourType = 2 and yt.YourDate = yt2.YourDate
join YourTable yt3 on yt3.YourType = 3 and yt.YourDate = yt3.YourDate
where yt.YourType = 1
I have a table
tblPay
(
CID bigint,
PartyIdID bigint,
PartyName varchar(50),
AgentName varchar(50),
Agent bigint,
Amount decimal(18,2),
RecAmount decimal(18,2),
OutStanding decimal(18,2)
)
I want to select sum of Amount,RecAmount group by PartyId and I also want to select Last Outstanding entry of each PartyID. For this i use following query.
SELECT DISTINCT CID,
Party.AccLedger_ID PartyID,
Party.AccLedger_Name PartyName,
Agent.AccLedger_Name AgentName,
Agent.AccLedger_ID AgentID,
sum(S.Amount) Amount,
Sum(S.RecAmount) RecAmount,
S.OutStanding
Group by PartyID,
Cid,
Party.AccLedger_ID,
Party.AccLedger_Name,
Agent.AccLedger_Name,
Agent.AccLedger_ID,
S.OutStanding
But i am unable to achieve the sum of Amount,RecAmount and Last Outstanding Record of each Party. Can someone help me here.
This is the Answer i got.
Assuming your records are inserted in tblPay.CID order, use a subquery
SELECT p.PartyID,
p.PartyName,
SUM(p.Amount) AS PartyAmount,
SUM(p.RecAmount) AS PartyRecAmount,
(SELECT TOP 1 OutStanding FROM tblPay p2 WHERE p.PartyID = p2.PartyId ORDER BY CID DESC) AS LastOutStanding
FROM tblPay p
GROUP BY p.PartyID, p.PartyName
or an OUTER APPLY:
SELECT p.PartyID,
p.PartyName,
SUM(p.Amount) AS PartyAmount,
SUM(p.RecAmount) AS PartyRecAmount,
lastRecord.OutStanding AS LastOutStanding
FROM tblPay p
OUTER APPLY
(
SELECT TOP 1 OutStanding
FROM tblPay p2
WHERE p.PartyID = p2.PartyId
ORDER BY CID DESC
) lastRecord
GROUP BY p.PartyID, p.PartyName
or, as JamieD77 suggests in his answer, use a CTE.
use a cte to get your tblPay information and join that to your other tables on the last record
WITH cte AS (
SELECT
CID,
PartyName,
SUM(Amount) OVER (PARTION BY PartyIdID) Amount,
SUM(RecAmount) OVER (PARTITION BY PartyIdID) RecAmount,
OutStanding,
-- only assuming your CID determines order since you have no date?
ROW_NUMBER() OVER (PARTITION BY PartyIdID ORDER BY CID DESC) Rn
FROM tblPay
)
SELECT Party.*,
Agent.*,
p.CID,
p.PartyName,
p.Amount,
p.RecAmount,
p.Oustanding
FROM Party JOIN Agent
JOIN cte p ON p.PartyIdID = Party.AccLedger_ID AND p.Rn = 1
I have 36 Sales tables each referred to one store:
st1.dbo.Sales
st2.dbo.Sales
...
st35.dbo.Sales
st36.dbo.Sales
Each record has the following key columns:
UserName, PostalCode, Location, Country, InvoiceAmount, ItemsCount, StoreID
Here is SQLFiddle
I need to copy into Customers table all Username (and their details) that are not already present into Customers
in case of duplicated it is required to use the fields of record where InvoiceAmount is MAX
I tried to build a query but looks too complicated and it is also wrong because in CROSS APPLY should consider the full list of Sales Tables
INSERT INTO Customers (.....)
SELECT distinct
d.UserName,
w.postalCode,
w.location,
W.country,
max(w.invoiceamount) invoiceamount,
max(w.itemscount) itemscount,
w.storeID
FROM
(SELECT * FROM st1.dbo.Sales
UNION
SELECT * FROM st2.dbo.Sales
UNION
...
SELECT * FROM st36.dbo.Sales) d
LEFT JOIN
G.dbo.Customers s ON d.Username = s.UserName
CROSS APPLY
(SELECT TOP (1) *
FROM s.dbo.[Sales]
WHERE d.Username=w.Username
ORDER BY InvoiceAmount DESC) w
WHERE
s.UserName IS NULL
AND d.username IS NOT NULL
GROUP BY
d.UserName, w.postalCode, w.location,
w.country, w.storeID
Can somebody please give some hints?
As a basic SQL query, I'd create a row_number in the inner subquery and then join to customers and then isolated the max invoice number for each customer not in the customer table.
INSERT INTO Customers (.....)
SELECT w.UserName,
w.postalCode,
w.location,
w.country,
w.invoiceamount,
w.itemscount,
w.storeID
FROM (select d.*,
row_number() over(partition by d.Username order by d.invoiceamount desc) rownumber
from (SELECT *
FROM st1.dbo.Sales
UNION
SELECT *
FROM st2.dbo.Sales
UNION
...
SELECT *
FROM st36.dbo.Sales
) d
LEFT JOIN G.dbo.Customers s
ON d.Username = s.UserName
WHERE s.UserName IS NULL
AND d.username IS NOT NULL
) w
where w.rownumber = 1
Using your fiddle this will select distinct usernames rows with max invoiceamount
with d as(
SELECT * FROM Sales
UNION
SELECT * FROM Sales2
)
select *
from ( select *,
rn = row_number() over(partition by Username order by invoiceamount desc)
from d) dd
where rn=1;
step 1 - use cte .
select username , invoiceamount ,itemscount from Sales
UNION all
select user name , invoiceamount ,itemscount from Sales
.....
...
step 2
next cte use group by and get max invoiceamount ,itemscount for user of last result set.
,cte2 as (
select user name , max (invoiceamount) as invoiceamount ,max(itemscount) as itemscount from cte)
step3
use left join with user table and find missing record and itemscount invoiceamount
I have a data table in sql server 2008 that I would like to select the top 1 out of each identifier:
The results shld looks like this during before and after:
Thus it should only select the 1st results if the same identifier do exist. Thanks a lot.
select distinct [Primary Identifier] from tbl
If you have entire records (other columns) instead of that single column, you can row number them and choose one.
select {list of columns}
from
(
select *, rn = row_number over (partition by [Primary Identifier]
order by 1/0)
from tbl
) X
where rn = 1;
order by 1/0 is arbitrary. If you need to choose a specific one from the "duplicates", for example the highest cost, you order by cost descending, i.e.
(partition by [Primary Identifier]
order by [cost] descending)
Just distinct them:
select distinct [primary identifier] from tablename
Or by grouping:
select [primary identifier] from tablename group by [primary identifier]
If more columns exist you can rank rows with window function:
;with cte as(select *, row_number() over(partition by [primary identifier] order by (select null)) rn from tablename)
select * from cte where rn = 1
Change order by (select null) to appropriate ordering column.
i think this will be an appropriate solution to your need-
;WITH cte AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY [Primary Identifier] ORDER BY [sort columns]) AS rowid
FROM [table]
)
SELECT *
FROM cte
WHERE rowid = 1