easiest way to substract in one column and add in other - sql

Basically I'm trying to move existances from one register to another, substracting from the origin point and adding it to the destiny. I have one table of "Stocks" like this:
country city value
AA 01 500
AA 02 400
BB 01 300
CC 01 100
CC 02 1000
CC 03 2000
And I am receiving a document of "movements" with a structure like this:
quantity originCountry originCity endCountry endCity
10 AA 01 CC 01
20 AA 01 BB 02
50 BB 01 CC 03
Is there a way to do this without creating two normalized tables and several queries.
Edit
The answer must be like this:
quantity originCountry originCity endCountry endCity
AA 01 470
AA 02 400
BB 01 250
CC 01 110
CC 02 1020
CC 03 2050
negative number and business rules aren't relevant

try this (isnull is for tsql):
SELECT country, city, value - isnull(p, 0) + isnull(m, 0) as q
FROM Stocks AS s
LEFT JOIN (SELECT originCountry, originCity, Sum(quantity) m FROM movements GROUP BY originCountry, originCity) AS mm
ON originCountry = s.country AND originCity = s.city
LEFT JOIN (SELECT endCountry, endCity, Sum(quantity) p FROM movements GROUP BY endCountry, endCity) AS mp ON
ON endCountry = s.country AND endCity = s.city
For MS ACCESS (add tag next time...):
this should by work:
SELECT country, city, value - Nz(p, 0) + Nz(m, 0) as q
FROM ((Stocks AS s
LEFT JOIN (SELECT originCountry, originCity, Sum(quantity) m FROM movements GROUP BY originCountry, originCity) AS mm
ON (originCountry = s.country AND originCity = s.city))
LEFT JOIN (SELECT endCountry, endCity, Sum(quantity) p FROM movements GROUP BY endCountry, endCity) AS mp ON
ON (endCountry = s.country AND endCity = s.city))
or this ((if you get error then use DSum function):
SELECT country, city, value
- Nz((
SELECT Sum(quantity) m
FROM movements
WHERE originCountry = s.country AND originCity = s.city
), 0) + Nz((
SELECT Sum(quantity) p
FROM movements
WHERE endCountry = s.country AND endCity = s.city
), 0) as q
FROM Stocks AS s

Related

getting results from date ranges for query mssql

i have a query that returns the following,
Employee : AA AA
Manager : BB BB
Dates : Jan 26 2022 8:00AM - Feb 2 2022 5:00PM
Days Requested : 6
Type Of Leave : Annual Leave
Department : Corporate Accounting & Tax Services
what i want to do to get a result for the days ( i am trying to create a pivot table in excel from the data ) that contains all the dates in between for example
Employee : AA AA
Manager : BB BB
Dates : Jan 26 2022 8:00AM
Days Requested : 6
Type Of Leave : Annual Leave
Department : Corporate Accounting & Tax Services
Employee : AA AA
Manager : BB BB
Dates : Jan 27 2022
Days Requested : 6
Type Of Leave : Annual Leave
Department : Corporate Accounting & Tax Services
Employee : AA AA
Manager : BB BB
Dates : Jan 28 2022
Days Requested : 6
Type Of Leave : Annual Leave
Department : Corporate Accounting & Tax Services
until the end and im stuck.
any help would be appreciated
;WITH cte AS(
SELECT
MYUSER.firstname AS Firstname,
MYUSER.lastname AS Lastname,
LEAVES.date_start,
LEAVES.date_end,
LEAVES.leave_days_requested AS DaysRequested,
LEAVES.status As Status,
TYPESLEAVES.types_ofLeaves,
Manager.user_id AS managerId,
tbl_department.name_department
FROM tbl_leaves AS LEAVES
LEFT OUTER JOIN tbl_user AS MYUSER ON MYUSER.id = LEAVES.tbl_user_id
LEFT OUTER JOIN tbl_types_of_leaves AS TYPESLEAVES ON LEAVES.tbl_type_of_leave = TYPESLEAVES.id
LEFT OUTER JOIN tbl_manager_user AS ManagerUser ON ManagerUser.tbl_user_id = MYUSER.id
LEFT OUTER JOIN tbl_manager AS Manager ON ManagerUser.tbl_manager_id = Manager.id
INNER JOIN tbl_department ON tbl_department.id = MYUSER.department_id
WHERE LEAVES.status = 'Pending')
Select
CONCAT(a.Firstname,' ',a.Lastname) AS Employee,
LTRIM(RTRIM(CONCAT(m.firstname, ' ', m.lastname))) AS Manager,
a.date_start,
a.date_end,
a.DaysRequested AS 'Days Requested',
a.types_ofLeaves AS 'Type Of Leave',
a.name_department AS 'Department'
FROM cte a
LEFT OUTER JOIN tbl_user m ON m.id = a.managerId
Thank you for your reply i created a date table indeed and joined it together and worked like a charm thxx
INNER JOIN tbl_leaves_dates d
on d.date >= a.date_start
and d.date <= a.date_end

Get SUM of Reference table rows

tblOrders
OrderId totalamount OrderStatus
01 1000 4
02 2000 4
tblCart
CartId OrderId NetPrice
05 01 400
06 01 650
07 02 750
08 02 1350
Desired Result:
OrderId totalamount OrderStatus NetPrice ItemCount
01 1000 4 1050 2
02 2000 4 2100 2
I want to achieve the JOIN of two tables with SUM(Netprice) based on OrderId Link
I tried doing this using SQL Query for a Stored Procedure but its giving ambiguous column error.
Please use below query,
select t1.OrderId, t1.totalamount, tl1.OrderStatus, sum(t2.NetPrice) as NetPrice ,
count(1) as ItemCount from
tblOrders t1
inner join tblCart t2
on (t1.OrderId = t2.OrderId)
group by t1.OrderId, t1.totalamount, tl1.OrderStatus;
If you want all columns from the orders table and summarized data from the other, you might find that apply is helpful:
select o.*, c.*
from tblOrders o outer apply
(select sum(c.netprice) as netprice, count(*) as itemcount
from tblCart c
where c.OrderId = o.OrderId
) c;

JOIN CTE's Grouped by Month/Year

I have multiple CTEs which result in the following common table structure:
Year | Month | Total_Purchases_Product_Line_X
These represent purchases grouped by month & year across several product lines.
Ex.)
SELECT * FROM cte_line_x
Year | Month | Total_Purchases_Product_Line_X
2018 01 256
2018 02 192
SELECT * FROM cte_line_y
Year | Month | Total_Purchases_Product_Line_Y
2018 01 76
2018 02 59
I'd like to create something like the following
Year | Month | Total_Purchases_Line_X | Total_Purchases_Line_Y | Total_Purchases_Line_Z
2018 01 256 76
2018 02 192 59
Where the total purchases of each product line is joined. However, I'm running into issues grouping the dates from each CTE after I have joined them together.
Here is what I've tried:
SELECT
cte_product_x.Month,
cte_product_x.Year,
cte_product_x.total as Total_X,
cte_product_y.total as Total_Y,
cte_product_z.total as Total_Z
FROM
cte_product_x
LEFT JOIN
cte_product_y ON
cte_product_y.year = cte_product_x.year
AND
cte_product_y.month = cte_product_x.month
LEFT JOIN
cte_product_z ON
cte_product_z.year = cte_product_x.year
AND
cte_product_z.month = cte_product_x.month
GROUP BY
cte_product_x.Month,
cte_product_x.Year
ORDER BY
cte_product_x.Month,
cte_product_x.Year
I tried changing my SELECT to:
SELECT
cte_product_x.Month,
cte_product_x.Year,
MAX(cte_product_x.total as Total_X),
MAX(cte_product_y as Total_Y),
MAX(cte_product_z as Total_Z)
However, it only worked for "Total_X". The counts for the other columns were the max value found for a grouped total for all months. I don't understand why.
Doesn't this work?
SELECT x.Month, x.Year, x.total as Total_X,
y.total as Total_Y, z.total as Total_Z
FROM cte_product_x x JOIN
cte_product_y y
ON y.year = x.year AND y.month = x.month JOIN
cte_product_z z
ON z.year = x.year AND z.month = x.month
ORDER BY x.Month, x.Year;
At least it works for your sample data.

Use grouped results from one table in condition of a LEFT JOIN on another table

This is my first post so please let me know if you need any more details.
I have 3 tables, one of which is a setup table [invrules] and then the other 2 are member data tables [basic] and [invinst]. What I want to do is to display the [invinst].[invcde] and [invinst].[instamt] columns using unique values for that scheme. The [invrules] table has the scheme code in the first 4 characters of the field [schkey]. What I have tried to do is to determine what these are by grouping them together for that scheme and then linking that to each LEFT JOIN I have created to give me the columns. This takes a very long time to run and obviously isn't very efficient.
I have to do this as a SELECT statement rather than using CTE or functions.
This was the attempt that took a long time to deliver the correct result (a couple of minutes):
select getdate() DateRun
,b.membno
,b.surnam
,b.scheme
,i1.invcde AS 'Investment Code 1'
,i1.instamt AS 'Investment Percentage 1'
,i2.invcde AS 'Investment Code 2'
,i2.instamt AS 'Investment Percentage 2'
,i3.invcde AS 'Investment Code 3'
,i3.instamt AS 'Investment Percentage 3'
from basic AS b
left join
(
select i.invcde, RANK() OVER (ORDER BY i.invcde) AS [RANK]
from invrules AS i
where left(schkey, 4) = 'ABCD'
and (i.enddte is null or i.enddte > getdate())
group by i.invcde
) AS [invcde_list_1] ON [invcde_list_1].Rank = 1
left join
(
select i.invcde, RANK() OVER (ORDER BY i.invcde) AS [RANK]
from invrules AS i
where left(schkey, 4) = 'ABCD'
and (i.enddte is null or i.enddte > getdate())
group by i.invcde
) AS [invcde_list_2] ON [invcde_list_2].Rank = 2
left join
(
select i.invcde, RANK() OVER (ORDER BY i.invcde) AS [RANK]
from invrules AS i
where left(schkey, 4) = 'ABCD'
and (i.enddte is null or i.enddte > getdate())
group by i.invcde
) AS [invcde_list_3] ON [invcde_list_3].Rank = 3
left join invinst AS i1 on b.membno = i1.membno and i1.invcde = [invcde_list_1].invcde and i1.contsrc = 'EE' and i1.enddte is null and [invcde_list_1].Rank = 1
left join invinst AS i2 on b.membno = i2.membno and i2.invcde = [invcde_list_2].invcde and i2.contsrc = 'EE' and i2.enddte is null and [invcde_list_2].Rank = 2
left join invinst AS i3 on b.membno = i3.membno and i3.invcde = [invcde_list_3].invcde and i3.contsrc = 'EE' and i3.enddte is null and [invcde_list_3].Rank = 3
where b.membno >= 15000
and b.scheme = 'ABCD'
order by b.membno
I tried to use a CROSS JOIN instead as the [invinst] table doesn't need to join to anything. This returned data from the [basic] table on each line and the matching data from the [invinst] table however there were additional rows because of the CROSS JOIN which gave Null values in the columns derived from the [invinst] table. This was the code:
select getdate() DateRun
,b.membno
,b.surnam
,b.scheme
,i1.invcde AS 'Investment Code 1'
,i1.instamt AS 'Investment Percentage 1'
,i2.invcde AS 'Investment Code 2'
,i2.instamt AS 'Investment Percentage 2'
,i3.invcde AS 'Investment Code 3'
,i3.instamt AS 'Investment Percentage 3'
from basic AS b
cross join
(
select i.invcde, RANK() OVER (ORDER BY i.invcde) AS [RANK]
from invrules AS i
where left(dcschkey, 4) = 'ABCD'
and (i.enddte is null or i.enddte > getdate())
group by i.invcde
) AS [invcde_list]
left join invinst AS i1 on b.membno = i1.membno and i1.invcde = [invcde_list].invcde and i1.contsrc = 'EE' and i1.enddte is null and [invcde_list].Rank = 1
left join invinst AS i2 on b.membno = i2.membno and i2.invcde = [invcde_list].invcde and i2.contsrc = 'EE' and i2.enddte is null and [invcde_list].Rank = 2
left join invinst AS i3 on b.membno = i3.membno and i3.invcde = [invcde_list].invcde and i3.contsrc = 'EE' and i3.enddte is null and [invcde_list].Rank = 3
where b.membno >= 15000
and b.scheme = 'ABCD'
order by b.membno
This is some test data:
[basic]
membno surnam scheme
14000 Jones ABCD
15000 Smith ABCD
15001 Henry ABCD
15002 Mabel ABCD
15003 McDonald ABCD
[invinst]
membno contsrc invcde instamt
14000 EE CD01 100
15000 EE CD01 50
15000 EE CD02 50
15000 ER CD01 50
15001 EE CD02 100
15002 EE CD01 100
15003 EE CD01 100
15003 ER CD01 100
[invrules]
schkey contsrc enddte invcde
ABCDCAT1 EE CD01
ABCDCAT1 ER CD01
ABCDCAT1 EE CD02
ABCDCAT1 ER CD02
ABCDCAT1 EE CD03
ABCDCAT1 ER CD03
ABCDCAT2 EE CD01
ABCDCAT2 ER CD01
ABCDCAT2 EE CD02
ABCDCAT2 ER CD02
ABCDCAT2 EE CD03
ABCDCAT2 ER CD03
ABCDCAT3 EE CD01
ABCDCAT3 EE CD02
ABCDCAT3 EE CD03
ABCDCAT4 EE CD01
ABCDCAT4 EE CD02
ABCDCAT4 EE CD03
ABBBCAT1 EE CD01
ABBBCAT1 EE CD02
ABBBCAT2 EE CD01
ABBBCAT3 EE CD01
I have had to modify the data somewhat to anonymise it but hopefully you will understand what I am trying to achieve.
Without using lots of sub queries is there a way I can summarise the [invrules] table for the scheme and use that within the LEFT JOINs that is more efficient? Or is there a different approach?
Thanks,
Darrell
If you do want to join 'basic' with 'invinst' which is in turn joined with 'invrules' - then simply do so!
Use nested joins, not 'flat'.
Basically, your first three joins join nothing.
UPD: try something like this
SELECT getdate() AS DateRun,
member_no
member_surname,
scheme,
investment_code,
investment_percentage,
percentage_rank
FROM (
SELECT
b.membno AS member_no,
b.surnam AS member_surname,
b.scheme AS scheme,
i.invcde AS Investment_Code,
i.instamt AS Investment_Percentage,
RANK() OVER (PARTITION BY b.membno ORDER BY i.instamt) AS percentage_rank
FROM basic AS b
LEFT JOIN invinst AS i ON b.membno = i.membno
LEFT JOIN invcde_list AS il ON i.invcde = il.invcde
WHERE b.membno >= 15000
AND b.scheme = 'ABCD'
AND i.contsrc = 'EE'
AND i1.enddte IS NULL
) as FinallyJoined
WHERE percentage_rank < 4;
that may not be exactly working, I don't have MSSQL in reach - but I hope you got the idea.

How to group my query by name? P_Name

select ID_Sale,SaleDate,Branch_Name,P_Name,P_UnitPrice,QuantitySold,
sum (QuantitySold*P_UnitPrice) over (order by ID_Sale asc) AS RunningTotal
from tblP_Sales
INNER JOIN tblProduct_With_Img
ON tblP_Sales.P_ID_Sales = tblProduct_With_Img.P_ID
INNER JOIN tblBranches
ON tblP_Sales.BranchID = tblBranches.BranchID
--this group is not working ? how to work this ?
group by P_Name
This is the result without the GROUP BY clause:
Sale_ID Sale_Date Branch Item Name Pric/unit qty RUNTotal
1056 2016-11-10 Ajman Afghani Pulaw With Meat 26 1 26
1057 2016-11-10 Ajman Sada Rice With Chicken Boti 24 2 74
1058 2016-11-11 Ajman Afghani Pulaw With Meat 26 1 100
I think you're looking to add "PARTITION BY "
select ID_Sale,SaleDate,Branch_Name,P_Name,P_UnitPrice,QuantitySold,
sum (QuantitySold*P_UnitPrice) over (PARTITION BY P_Name order by ID_Sale asc) AS RunningTotal
from tblP_Sales
INNER JOIN tblProduct_With_Img
ON tblP_Sales.P_ID_Sales = tblProduct_With_Img.P_ID
INNER JOIN tblBranches
ON tblP_Sales.BranchID = tblBranches.BranchID