I have two different sales tables and i want to SUM the quantity and FEEs for storeX and storeY from book_sales(isa), with quantity from all sales in the financial_report(xo). Im having trouble with both, and i dont know which JOIN i should use. The quantity calculation does not give any results and the fee calculation gives a number that is way to high. What is wrong here?
Please check my procedure if you have time:
ALTER PROCEDURE [dbo].[Report] #startDate VARCHAR(10),
#endDate VARCHAR(10)
AS
SELECT BB.name AS 'Publisher',
Sum(CASE
WHEN isa.report_source = 'storeX'
or isa.report_source = 'storeY' THEN
isa.quantity * xo.quantity
END) AS 'Total quantity',
Sum(CASE
WHEN isa.sales_price > 69
AND isa.report_source = 'storeX' THEN
6 * 1.25 * isa.quantity
WHEN isa.sales_price <= 69
AND isa.report_source = 'storeX' THEN
3 * 1.25 * isa.quantity
WHEN isa.sales_price <= 19
AND isa.report_source = 'storeX' THEN
1 * 1.25 * isa.quantity
WHEN isa.sales_price > 69
AND isa.report_source = 'storeY' THEN
6 * 1.25 * isa.quantity
WHEN isa.sales_price <= 69
AND isa.report_source = 'storeY' THEN
3 * 1.25 * isa.quantity
WHEN isa.sales_price <= 19
AND isa.report_source = 'storeY' THEN
1 * 1.25 * isa.quantity
WHEN xo.sales_price > 69
AND bb.country = 'NOR' THEN 6 * 1.25 * xo.quantity
WHEN xo.sales_price <= 69
AND bb.country = 'NOR' THEN 3 * 1.25 * xo.quantity
WHEN xo.sales_price > 69
AND bb.country <> 'NOR' THEN 6 * xo.quantity
WHEN xo.sales_price <= 69
AND bb.country <> 'NOR' THEN 3 * xo.quantity
END) AS 'Fee inc VAT(tot)'
FROM book_sales AS isa
INNER JOIN store AS BV
ON bv.store_id = isa.store_id
INNER JOIN financial_report AS xo
ON xo.identifiers = isa.identifiers
LEFT OUTER JOIN publisher AS BB
ON bb.publisher_id = bk.publisher_id
WHERE isa.sales_date >= CONVERT(DATETIME, #startDate, 20)
AND isa.sales_date < Dateadd(day, 1, ( CONVERT(DATETIME, #endDate, 20
) ))
GROUP BY bb.name,
bb.country
There are a few problems to sort out :
1- The total might be affected by NULL values, I suggest:
Sum(CASE
WHEN isa.report_source = 'storeX'
or isa.report_source = 'storeY' THEN
isnull(isa.quantity,0) * isnull(xo.quantity,0)
ELSE 0 END) AS 'Total quantity',
2- The case evaluation should be in order. For example:
WHEN isa.sales_price <= 69
AND isa.report_source = 'storeX' THEN
3 * 1.25 * isa.quantity
WHEN isa.sales_price <= 19
AND isa.report_source = 'storeX' THEN
1 * 1.25 * isa.quantity
<= 19 will never happen because it is also <= 69 and therefore the <=69 will evaluate true.
3- Cardinality between tables might be 1 to many so the values are summed multiple times when they shouldn't. You may need to select distinct or aggregate or flatten out some of the tables in the join to avoid this issue.
Related
I have a query in MYSQL which count products from specific vendor with product_status like Live, Pause, soldout, Partial-Soldout etc. Query include Subquery but works perfect in Mysql. For Redshift (Postgre v8.x) it gives error correlated subquery pattern is not supported due to internal error
Query (POSTGRES)
SELECT COUNT(CASE WHEN (vendor_id = 6 AND status = 1) THEN 1 ELSE NULL END) AS "vex",
COUNT(CASE WHEN (vendor_id = 6 AND status = 1 AND p.p_id IN (SELECT pov.p_id FROM product_option_value pov WHERE pov.p_id AND p.quantity != pov.quantity AND pov.quantity = 0 GROUP BY pov.p_id)) THEN 1 ELSE NULL END) AS "vex-Partial-Soldout",
COUNT(CASE WHEN (vendor_id = 6 AND status = 1 AND p.quantity = 0) THEN 1 ELSE NULL END) AS "vex-Soldout",
COUNT(CASE WHEN (vendor_id = 5 AND status = 1) THEN 1 ELSE NULL END) AS "vey-DXB",
COUNT(CASE WHEN (vendor_id = 5 AND status = 1 AND p.p_id IN (SELECT pov.p_id FROM product_option_value pov WHERE pov._id AND p.quantity != pov.quantity AND pov.quantity = 0 GROUP BY pov.p_id)) THEN 1 ELSE NULL END) AS "vey-Partial-Soldout",
COUNT(CASE WHEN (vendor_id = 5 AND status = 1 AND p.quantity = 0) THEN 1 ELSE NULL END) AS "vey-Soldout"
FROM product p
Table Structure
//Product p table
* p_id * model * vendor_id * status * Quantity *
* 1001 * HB1 * 1 * 1 * 10 *
* 1002 * HB2 * 6 * 1 * 17 *
* 1003 * HB3 * 5 * 1 * 19 *
* 1004 * HB4 * 2 * 1 * 3 *
* 1005 * HB5 * 1 * 1 * 8 *
* 1006 * HB6 * 6 * 1 * 55 *
* 1007 * HB7 * 3 * 1 * 32 *
* 1008 * HB8 * 5 * 1 * 6 *
* 1009 * HB9 * 5 * 1 * 10 *
//product_option_value pov table
* pov_id * p_id * opt_id * quantity *
* 1 * 1001 * 11 * 10 *
* 2 * 1002 * 11 * 17 *
* 3 * 1003 * 11 * 0 *
* 4 * 1004 * 11 * 3 *
* 5 * 1005 * 11 * 8 *
* 6 * 1006 * 11 * 0 *
* 7 * 1007 * 11 * 32 *
* 8 * 1008 * 11 * 6 *
* 9 * 1009 * 11 * 0 *
Group by is necessary in subquery so left join is also not solving the issue.
The logic in the subqueries in quite painful to follow, so I'm not sure I have it 100% correct. For instance, the sample data only seems to have one row per product, but I don't know if that is really the case. Or what opt_id is, because that would seem to be useful in a query that uses the pov table.
That said, it appears that you just need JOIN and GROUP BY to get what you want -- a query that is much simpler and should be faster in any database.
In the following query, I have also split the two vendors on different rows. That is at least helpful to get the logic correct:
SELECT vendor_id,
COUNT(DISTINCT p.p_id) AS num_products,
SUM(CASE WHEN p.quantity <> pov.quantity AND pov.quantity = 0 THEN 1 ELSE 0 END) as partial_soldout,
COUNT(DISTINCT CASE WHEN p.quantity = 0 THEN p.p_id END) as soldout
FROM product p LEFT JOIN
product_option_value pov
ON pov.p_id = p.p_pid
WHERE p.vendor_id IN (5, 6) AND p.status = 1
GROUP BY p.vendor_id;
You have to remove the "pov.p_id AND" just after the where clause from the two expressions which calculate "vex-Partial-Soldout" and "vey-Partial-Soldout". Then it is working on postgreSQL9.6 and output is matching with your sqlserver version output.
SELECT COUNT(CASE WHEN (vendor_id = 6 AND status = 1) THEN 1 ELSE NULL END) AS "vex",
COUNT(CASE WHEN (vendor_id = 6 AND status = 1 AND p.p_id IN (SELECT pov.p_id FROM product_option_value pov WHERE p.quantity != pov.quantity AND pov.quantity = 0 GROUP BY pov.p_id)) THEN 1 ELSE NULL END) AS "vex-Partial-Soldout",
COUNT(CASE WHEN (vendor_id = 6 AND status = 1 AND p.quantity = 0) THEN 1 ELSE NULL END) AS "vex-Soldout",
COUNT(CASE WHEN (vendor_id = 5 AND status = 1) THEN 1 ELSE NULL END) AS "vey-DXB",
COUNT(CASE WHEN (vendor_id = 5 AND status = 1 AND p.p_id IN (SELECT pov.p_id FROM product_option_value pov WHERE p.quantity != pov.quantity AND pov.quantity = 0 GROUP BY pov.p_id)) THEN 1 ELSE NULL END) AS "vey-Partial-Soldout",
COUNT(CASE WHEN (vendor_id = 5 AND status = 1 AND p.quantity = 0) THEN 1 ELSE NULL END) AS "vey-Soldout"
FROM product p
Note: I did not paid much attention to your logic, as a best practice please avoid writing select statements within your select.
I need to get running totals between 2 dates in my sql server table and update the records simultaneoulsy. My data is as below and ordered by date,voucher_no
DATE VOUCHER_NO OPEN_BAL DEBITS CREDITS CLOS_BAL
-------------------------------------------------------------------
10/10/2017 1 100 10 110
12/10/2017 2 110 5 105
13/10/2017 3 105 20 125
Now if i insert a record with voucher_no 4 on 12/10/2017 the output should be like
DATE VOUCHER_NO OPEN_BAL DEBITS CREDITS CLOS_BAL
------------------------------------------------------------------
10/10/2017 1 100 10 110
12/10/2017 2 110 5 105
12/10/2017 4 105 4 109
13/10/2017 3 109 20 129
I have seen several examples which find running totals upto a certain date but not between 2 dates or from a particular date to end of file
You should consider changing your database structure. I think it will be better to keep DATE, VOUCHER_NO, DEBITS, CREDITS in one table. And create view to calculate balances. In that case you will not have to update table after each insert. In this case your table will look like
create table myTable (
DATE date
, VOUCHER_NO int
, DEBITS int
, CREDITS int
)
insert into myTable values
('20171010', 1, 10, null),( '20171012', 2, null, 5)
, ('20171013', 3, 20, null), ('20171012', 4, 4, null)
And view will be
;with cte as (
select
DATE, VOUCHER_NO, DEBITS, CREDITS, bal = isnull(DEBITS, CREDITS) * case when DEBITS is null then -1 else 1 end
, rn = row_number() over (order by DATE, VOUCHER_NO)
from
myTable
)
select
a.DATE, a.VOUCHER_NO, a.DEBITS, a.CREDITS
, OPEN_BAL = sum(b.bal + case when b.rn = 1 then 100 else 0 end) - a.bal
, CLOS_BAL = sum(b.bal + case when b.rn = 1 then 100 else 0 end)
from
cte a
join cte b on a.rn >= b.rn
group by a.DATE, a.VOUCHER_NO, a.rn, a.bal, a.DEBITS, a.CREDITS
Here's another solution if you can not change your db structure. In this case you must run update statement each time after inserts. In both cases I assume that initial balance is 100 while recalculation
create table myTable (
DATE date
, VOUCHER_NO int
, OPEN_BAL int
, DEBITS int
, CREDITS int
, CLOS_BAL int
)
insert into myTable values
('20171010', 1, 100, 10, null, 110)
,( '20171012', 2, 110, null, 5, 105)
, ('20171013', 3, 105, 20, null, 125)
, ('20171012', 4, null, 4, null, null)
;with cte as (
select
DATE, VOUCHER_NO, DEBITS, CREDITS, bal = isnull(DEBITS, CREDITS) * case when DEBITS is null then -1 else 1 end
, rn = row_number() over (order by DATE, VOUCHER_NO)
from
myTable
)
, cte2 as (
select
a.DATE, a.VOUCHER_NO
, OPEN_BAL = sum(b.bal + case when b.rn = 1 then 100 else 0 end) - a.bal
, CLOS_BAL = sum(b.bal + case when b.rn = 1 then 100 else 0 end)
from
cte a
join cte b on a.rn >= b.rn
group by a.DATE, a.VOUCHER_NO, a.rn, a.bal
)
update a
set a.OPEN_BAL = b.OPEN_BAL, a.CLOS_BAL = b.CLOS_BAL
from
myTable a
join cte2 b on a.DATE = b.DATE and a.VOUCHER_NO = b.VOUCHER_NO
The SQL code I have is shown below. The issue I am having is with calculating the sum of the Value of each Number. Doing them separately is okay and works when the Number is specified in the code but not when the number is not specified and I want the sum calculation to work for all separate Numbers.
{ Looks to calculate the value at the point of Tax. So all values before the tax date are ignored and all unit entries that occurred after the tax date are subtracted off the total units. The tax units are also subtracted off. The total units at point of tax, for each fund, is multiplied by the price at point of tax. The values of the funds are summed giving a total value for the number. }
This is the code I have done:
SELECT DISTINCT
A.Number,
CAST(ROUND((SELECT SUM(Val)
FROM (SELECT((S.TotalUnits -
(SELECT SUM(Units)
FROM TableIH AS F WHERE F.Fund = S.Fund AND F.Number = S.Number
AND F.Date >= (SELECT MIN(Date) FROM TableIH AS D
WHERE D.Fund= F.Fund AND D.Number = F.Number AND D.Entry = 'Tax' )
)
) * T.Price
) AS Val
FROM TableIH T
INNER JOIN TableID S
ON T.Number = S.Number
WHERE S.TotalUnits > 0 AND T.Fund = S.Fund
AND T.Price = ANY (SELECT (Price)
FROM TableIH AS E WHERE E.Entry = 'Tax' AND E.Number = T.Number)
)t
)
,2)
AS DECIMAL (25,2)) AS "Value"
FROM
TableIH A
INNER JOIN TableID C
ON C.Number = A.Number
group by A.Number
Tables are:
TableIH:
Number Fund Entry Units Price Date
12 YY RE 6 0.2 2015-02-02
12 YY Tax -10 0.1 2015-01-13
13 XX RE 6 0.2 2015-02-12
13 XX Tax -20 0.05 2014-12-11
13 MM Tax -25 0.6 2014-12-10
13 MM RE 8 0.2 2013-11-02
TableID
Number Fund TotalUnits
12 YY 2000
13 XX 1500
13 MM 500
DESIRED RESULT:
Number Value
12 [ ((2000) - (6) - (-10)) * 0.1 ] = 200.4
13 [ ((1500) - (6) - (-20)) * 0.05 ] + [ ((500) - (-25)) * 0.6 ] = 390.7
BUT GETTING: - Result is summing all instead of dividing it between each number.
Number Value
12 [ (200.4 + 390.7] = 591.1
13 [ (200.4 + 390.7] = 591.1
Any help with the errors would be greatly appreciated
Good evening, The problem is in your third nested subquery, you have no group on your sum, so it's summing all units, so you'll only get one total:
SELECT SUM(Units)
FROM TableIH AS F WHERE F.Fund = S.Fund AND F.Number = S.Number
AND F.Date >= (SELECT MIN(Date) FROM TableIH AS D
WHERE D.Fund= F.Fund AND D.Number = F.Number AND D.Entry = 'Tax'
This is my Select query
Select
snpc_stats.gamedetail.Player,
Sum(snpc_stats.gamedetail.Points + snpc_stats.gamedetail.Hits) As TotalPoints,
COUNT(*) As 'Games Played',
Sum(If(snpc_stats.gamedetail.Finish = 1, 1, 0)) As Wins,
Sum(If(snpc_stats.gamedetail.Finish = 2, 1, 0)) As Second,
Sum(If(snpc_stats.gamedetail.Finish = 3, 1, 0)) As Third,
Round(Avg(snpc_stats.gamedetail.Finish),2) As 'Average Finish',
Sum(snpc_stats.gamedetail.Hits) As `Total Hits`,
Sum(snpc_stats.gamedetail.ChampFund) As ChampFund,
Sum(snpc_stats.games.BuyIn) + (snpc_stats.gamedetail.ChampFund) As Cost,
Sum(snpc_stats.gamedetail.Winnings) As Winnings,
Sum(snpc_stats.gamedetail.Winnings) - (snpc_stats.games.BuyIn) - (snpc_stats.gamedetail.ChampFund) As 'Total Winnings',
COUNT(snpc_stats.games.Round) As round
From
snpc_stats.gamedetail Inner Join
snpc_stats.games On snpc_stats.games.GameID =
snpc_stats.gamedetail.GamesID
Where
snpc_stats.games.Season = '2015 Season'
Group By
snpc_stats.gamedetail.Player, snpc_stats.games.Season
Order By
TotalPoints Desc
After the TotalPoints Column I like to add a column that show the amount of points each player trails behind the leader, like the table below:
Rank | Player | Total Points | Points Behind
1 Bill 164 -
2 Al 152 -12
3 Ed 151 -13
4 Jill 123 -41
5 Bob 121 -43
6 Joe 102 -62
7 Dave 82 -82
8 Rob 60 -104
9 Doug 60 -104
10 Don 51 -113
11 Dan 30 -134
Any help would be so appreciated!
solution (1)If you are using a server side language that connects to the database and runs the query, you can preserve total points value of first row in some variable and subtracting all the following rows from it
solution (2)make a sub query that returns total points of the first row and use it in the selection part of the query
Select
snpc_stats.gamedetail.Player,
Sum(snpc_stats.gamedetail.Points + snpc_stats.gamedetail.Hits) As TotalPoints,
(Sum(snpc_stats.gamedetail.Points + snpc_stats.gamedetail.Hits) - (Select Sum(snpc_stats.gamedetail.Points + snpc_stats.gamedetail.Hits) From snpc_stats.gamedetail Inner Join snpc_stats.games On snpc_stats.games.GameID = snpc_stats.gamedetail.GamesID Where snpc_stats.games.Season = '2015 Season' Group By snpc_stats.gamedetail.Player, snpc_stats.games.Seaso Order By Sum(snpc_stats.gamedetail.Points + snpc_stats.gamedetail.Hits) Desc Limit 1)) As Points Behind,
COUNT(*) As 'Games Played',
Sum(If(snpc_stats.gamedetail.Finish = 1, 1, 0)) As Wins,
Sum(If(snpc_stats.gamedetail.Finish = 2, 1, 0)) As Second,
Sum(If(snpc_stats.gamedetail.Finish = 3, 1, 0)) As Third,
Round(Avg(snpc_stats.gamedetail.Finish),2) As 'Average Finish',
Sum(snpc_stats.gamedetail.Hits) As `Total Hits`,
Sum(snpc_stats.gamedetail.ChampFund) As ChampFund,
Sum(snpc_stats.games.BuyIn) + (snpc_stats.gamedetail.ChampFund) As Cost,
Sum(snpc_stats.gamedetail.Winnings) As Winnings,
Sum(snpc_stats.gamedetail.Winnings) - (snpc_stats.games.BuyIn) - (snpc_stats.gamedetail.ChampFund) As 'Total Winnings',
COUNT(snpc_stats.games.Round) As round
From
snpc_stats.gamedetail Inner Join
snpc_stats.games On snpc_stats.games.GameID =
snpc_stats.gamedetail.GamesID
Where
snpc_stats.games.Season = '2015 Season'
Group By
snpc_stats.gamedetail.Player, snpc_stats.games.Season
Order By
TotalPoints Desc
I am trying to get the total 'capacity' for each station but I keep getting the error message "#1630 - FUNCTION tflBikes.COUNT does not exist":
SELECT Count(ts.capacity),
tbu.stationid,
ts.name,
ts.easting,
ts.northing,
tbu.t,
Round(Avg(availablebikes), 1) AS Average,
Round(Avg(availablebikes) / capacity * 100, 1) AS Percentage,
ts.postcode
FROM tflbikeusage tbu,
tflstations ts
WHERE tbu.stationid = ts.usageid
AND ts.easting = Abs(easting -
(SELECT easting
FROM tflstations
WHERE name = 'Hatton Garden, Holborn')) < 750 * 0.5
AND ts.northing = Abs(northing -
(SELECT northing
FROM tflstations
WHERE name = 'Hatton Garden, Holborn')) < 750 * 0.5
AND Hour(t) BETWEEN 10 AND 22
GROUP BY ts.capacity HAVING COUNT (ts.capacity) =
(SELECT Sum(capacityno)
FROM
(SELECT COUNT(ts.capacity) AS capacityNo,
tbu.stationid,
ts.name,
ts.easting,
ts.northing,
tbu.t,
Round(Avg(availablebikes), 1) AS Average,
Round(Avg(availablebikes) / capacity * 100 , 1) AS Percentage,
ts.postcode
FROM tflbikeusage tbu,
tflstations ts
WHERE tbu.stationid = ts.usageid
AND ts.easting = Abs(easting -
(SELECT easting
FROM tflstations
WHERE name = 'Hatton Garden, Holborn')) < 750 * 0.5
AND ts.northing = Abs(northing -
(SELECT northing
FROM tflstations
WHERE name = 'Hatton Garden, Holborn')) < 750 * 0.5
AND Hour(t) BETWEEN 10 AND 22
GROUP BY ts.capacity
ORDER BY percentage DESC)derivedTable)
You are aliasing tflbikesUsage to "tbu", so you should be using that going forward.