Sum Group By Column - sql

I have a column (PL.UNITS) that I need to Total at the bottom of the results of a query, is it possible to sum PL.UNITS that is already summed?
Please see query below.
SELECT ID.DUEDATE AS [DUE DATE], CD.RENEWALDATE, CD.RENEWALSTATUS, CD.CONTRACTNUMBER, L.LOCNAME, L.LOCADDRESS1, L.LOCADDRESS2, L.LOCADDRESS3, L.LOCADDRESS4, L.POSTCODE, SUM(PL.UNITS) AS UNITS from CLIENTDETAILS CD
INNER JOIN LOCATIONS L ON CD.CLIENTNUMBER = L.CLIENTNUMBER
INNER JOIN ITEMDETAILS ID ON L.LOCNUMBER = ID.LOCNUMBER
INNER JOIN PLANT PL ON ID.CODE = PL.CODE
WHERE L.OWNER = 210 and L.STATUSLIVE = 1 and ID.DUEDATE > '01/01/2017'
GROUP BY ID.DUEDATE, CD.RENEWALDATE, CD.RENEWALSTATUS, CD.CONTRACTNUMBER, L.LOCNAME, L.LOCADDRESS1, L.LOCADDRESS2, L.LOCADDRESS3, L.LOCADDRESS4, L.POSTCODE

It's probably best to do this sort of thing in front end development. Nevertheless, here is an example (quick and dirty, but shows the idea) for sql-server:
SELECT COALESCE(a.id, 'total') AS id
, SUM(a.thing) AS thing_summed
FROM (
SELECT '1' id
, 1 thing
UNION
SELECT '2'
, 2 thing
UNION
SELECT '1'
, 3 thing
) AS a
GROUP BY ROLLUP(a.id)
Result:
+-------+--------------+
| id | thing_summed |
+-------+--------------+
| 1 | 4 |
| 2 | 2 |
| total | 6 |
+-------+--------------+

Related

Select rows where main value has disabled status and sub value is active

I have a table containing customer agreement numbers and a status field indicating whether that agreement is active or not - 1 for active, 0 for disabled.
A main customer number contains 5 digits, from which other subagreements can be made. These other agreements are characterized by a 10 digit number, the first 5 coming from the main number and the last 5 autogenerated.
Note that not all main agreements necessarily have subagreements.
Heres a simplified snippet of the table I currently get from my query:
+-------------+----------+------------+--+
| CustNumber| CustName | CustStatus | |
+-------------+----------+------------+--+
|12345 | Cust1 | 1 | |
|1234500001 | Cust1 | 1 | |
|1234500002 | Cust1 | 0 | |
|12346 | Cust2 | 0 | |<---
|1234600001 | Cust2 | 1 | |<---
|1234600002 | Cust2 | 0 | |
+-------------+----------+------------+--+
Query:
SELECT
custnumber,
custstatus,
custname
FROM table
WHERE LEFT(custnumber, 5) IN (
SELECT LEFT(custnumber, 5)
FROM table
GROUP BY LEFT(custnumber, 5)
HAVING Count(*) > 1
)
ORDER BY custnumber,
custstatus DESC;
From here I'm pretty lost. I'm thinking something along the lines of an inner join on a subquery but I'm really not sure.
What I'm looking for is a query that selects rows with subagreement numbers that are active but where the main agreement number is disabled.
I'm new to SQL and have spend a good while searching around for similar questions, but I actually don't know how to describe this problem in a google-friendly manner.
Join the table with itself - I am using a WITH clause for readability, but that is not necessary - and check the statuses.
with main_rows as
(
select custnumber as main_number, custname, custstatus
from mytable
where length(custnumber) = 5
)
, sub_rows as
(
select
left(custnumber, 5) as main_number,
right(custnumber, 5) as sub_number,
custname,
custstatus
from mytable
where length(custnumber) = 10
)
select
main_number,
m.custname as main_name,
s.sub_number,
s.custname as sub_name
from main_rows m
join sub_rows s using (main_number)
where m.custstatus = 0 and s.custstatus = 1
order by main_number, s.sub_number;
And here is the same thing, but shorter and just not as talkative :-)
select *
from mytable m
join mytable s on s.custnumber like m.custnumber || '_____'
where m.custstatus = 0 and s.custstatus = 1
order by s.custnumber;
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=5873044787e5fd3f32f7648dbc54a7b0
with data (CustNumber, CustName, CustStatus) as(
Select '12345' ,'Cust1',1 union all
Select '1234500001' ,'Cust1',1 union all
Select '1234500002' ,'Cust1',0 union all
Select '12346' ,'Cust2',0 union all
Select '1234600001' ,'Cust2',1 union all
Select '1234600002' ,'Cust2',0
)
,subagg (k,CustNumber, CustName, CustStatus) as(
select Left(CustNumber,5) k,CustNumber, CustName, CustStatus
from data
where len(CustNumber)=10
and CustStatus = 1
)
select s.CustNumber ActiveSunCustomer, d.CustNumber InactivePrimaryCustomer
from subagg s
join data d on d.CustNumber=s.k and d.CustStatus = 0

After joining two queries (each having different columns) with UNION I'm getting only one column

I have joined two queries with UNION keyword (Access 2016). It looks like that:
SELECT ITEM.IName, Sum(STOCK_IN.StockIn) AS SumOfIN
FROM ITEM INNER JOIN STOCK_IN ON ITEM.IName = STOCK_IN.IName
GROUP BY ITEM.IName
UNION SELECT ITEM.IName, Sum(STOCK_OUT.StockOut) AS SumOfOut
FROM ITEM INNER JOIN STOCK_OUT ON ITEM.IName = STOCK_OUT.IName
GROUP BY ITEM.IName
I get the following result:
IName | SumOfIN
----------------
Abis Nig | 3
Abrotanum | 1
Acid Acet | 2
Aconite Nap | 2
Aconite Nap | 3
Antim Crud | 3
Antim Tart | 1
But I want the following result:
IName | SumOfIN | SumOfOut
----------------
Abis Nig | 3 | 0
Abrotanum | 1 | 0
Acid Acet | 2 | 0
Aconite Nap | 2 | 3
Antim Crud | 0 | 3
Antim Tart | 0 | 1
Can anyone tell me what changes should I make here?
You need to add dummy values for the third column where they don't exist in the table you are UNIONing. In addition, you need an overall SELECT/GROUP BY since you can have values for both StockIn and StockOut:
SELECT IName, SUM(SumOfIN), Sum(SumOfOut)
FROM (SELECT ITEM.IName, Sum(STOCK_IN.StockIn) AS SumOfIN, 0 AS SumOfOut
FROM ITEM INNER JOIN STOCK_IN ON ITEM.IName = STOCK_IN.IName
GROUP BY ITEM.IName
UNION ALL
SELECT ITEM.IName, 0, Sum(STOCK_OUT.StockOut)
FROM ITEM INNER JOIN STOCK_OUT ON ITEM.IName = STOCK_OUT.IName
GROUP BY ITEM.IName) s
GROUP BY IName
Note that column names in the result table are all taken from the first table in the UNION, so we must name SumOfOut in that query.
You can do this query without UNION at all:
select i.iname, si.sumofin, so.sumofout
from (item as i left join
(select si.iname, sum(si.stockin) as sumofin
from stock_in as si
group by si.iname
) as si
on si.iname = i.iname
) left join
(select so.iname, sum(so.stockout) as sumofout
from stock_out as so
group by so.iname
) as so
on so.iname = i.iname;
This will include items that have no stock in or stock out. That might be a good thing, or a bad thing. If a bad thing, then add:
where si.sumofin > 0 or so.sumofout > 0
If you are going to use union all, then you can dispense with the join to items entirely:
SELECT IName, SUM(SumOfIN), Sum(SumOfOut)
FROM (SELECT si.IName, Sum(si.StockIn) AS SumOfIN, 0 AS SumOfOut
FROM STOCK_IN as si
GROUP BY si.INAME
UNION ALL
SELECT so.IName, 0, Sum(so.StockOut)
STOCK_OUT so
GROUP BY so.IName
) s
GROUP BY IName;
The JOIN would only be necessary if you had stock items that are not in the items table. That would be a sign of bad data modeling.

How to get Order numbers where collection number has a top and a corresponding bottom?

This is how the main order table looks :-
| Order_num | Collection_Num |
+--------------+----------------+
| 20143045585 | 123456 |
| 20143045585 | 789012 |
| 20143045585 | 456897 |
| 20143758257 | 546465 |
+--------------+----------------+
These are the collections:-
| tops | bottom |
+--------------+----------------+
| 353735 | 745758 |
| 123456 | 789012 |
| 456456 | 456897 |
| 323456 | 546465 |
+--------------+----------------+
Desired Output:-
| Order_num |
+--------------+
| 20143045585 |
Here Order number 20143045585 has both a top and a bottom from the same row in table number 2 (each row in 2nd table forms a particular combination called 'A Collection' i.e. 1 top and corresponding bottom ).
What I want to know -
All the order numbers which have a top and a corresponding bottom in 'Collection_num' column.
Can anyone help me with a SQL code for this ?
Let me know if any of this is unclear.
select Order_num
From table_1 as A
where exists
(select tops from table_2 as B where B.tops = A.Collection_num)
AND
(select bottom from table2 as B where B.bottom = A.Collection_num)
I am assuming you just have the first table of data and each order can only have the two relevant collections or less. Perhaps:
select T1.Order_Num
,T1.Collection_Num AS Tops
,T2.Collection_Num AS Bottom
from Table1 T1
inner join Table1 T2
on T1.Order_Num = T2.Order_Num
and T1.Collection_Num < T2.Collection_Num
order by T1.Order_Num
You can try using subquery
select distinct order_num from #yourorder where collection_num
in (select tops from #yourcollections)
and order_num in
( select order_num from #yourorder where collection_num in
(select bottom from #yourcollections) )
Pretty sure that something like this should work for you. I am just using the ctes here to create the test data so it can be queried.
with Orders (OrderNum, CollectionNum) as
(
select 20143045585, 123456 union all
select 20143045585, 789012 union all
select 20143045585, 456897 union all
select 20143758257, 546465
)
, Collections (CollectionID, tops, bottoms) as
(
select 1, 353735, 745758 union all
select 2, 123456, 789012 union all
select 3, 456456, 456897 union all
select 4, 323456, 546465
)
select o.OrderNum
, t.tops
, b.bottoms
from Orders o
join Collections t on t.tops = o.CollectionNum
join
(
select o.OrderNum
, b.bottoms
, b.CollectionID
from Orders o
join Collections b on b.bottoms = o.CollectionNum
) b on b.CollectionID = t.CollectionID
Here is the query that I used:
Select *
From (select A.Order_num, B.Coll_ID, B.Bottoms from Orders_table as A
Join Collections_Table as B
on A.Collection_num = B.Bottoms
) as C
join
(select K.Order_num, M.Coll_ID, M.Tops from Orders_table as K
Join Collections_Table as M
on A.Collection_num = B.Tops
) as D
on C.Orders_B = D.Orders_Num AND C.Coll_ID = D.Coll_ID
)

Joining Table A and B to get elements of both

I have two tables:
Table 'bookings':
id | date | hours
--------------------------
1 | 06/01/2016 | 2
1 | 06/02/2016 | 1
2 | 06/03/2016 | 2
3 | 06/03/2016 | 4
Table 'lookupCalendar':
date
-----
06/01/2016
06/02/2016
06/03/2016
I want to join them together so that I have a date for each booking so that the results look like this:
Table 'results':
id | date | hours
--------------------------
1 | 06/01/2016 | 2
1 | 06/02/2016 | 1
1 | 06/03/2016 | 0 <-- Added by query
2 | 06/01/2016 | 0 <-- Added by query
2 | 06/02/2016 | 0 <-- Added by query
2 | 06/03/2016 | 2
3 | 06/01/2016 | 0 <-- Added by query
3 | 06/02/2016 | 0 <-- Added by query
3 | 06/03/2016 | 4
I have tried doing a cross-apply, but that doesn't get me there, neither does a full join. The FULL JOIN just gives me nulls in the id column and the cross-apply gives me too much data.
Is there a query that can give me the results table above?
More Information
It might be beneficial to note that I am doing this so that I can calculate an average hours booked over a period of time, not just the number of records in the table.
Ideally, I'd be able to do
SELECT AVG(hours) AS my_average, id
FROM bookings
GROUP BY id
But since that would just give me a count of the records instead of the count of the days I want to cross apply it with the dates. Then I think I can just do the query above with the results table.
select i.id, c.date, coalesce(b.hours, 0) as hours
from lookupCalendar c
cross join (select distinct id from bookings) i
left join bookings b
on b.id = i.id
and b.date = c.date
order by i.id, c.date
Try this:
select c.date, b.id, isnull(b.hours, 0)
from lookupCalendar c
left join bookings b on b.date = c.date
LookupCalendar is your main table because you want the bookings against each date, irrespective of whether there was a booking on that date or not, so a left join is required.
I am not sure if you need to include b.id to solve your actual problem though. Wouldn't you just want to get the total number of hours booked against each date like this, to then calculate the average?:
select c.date, sum(isnull(b.hours, 0))
from lookupCalendar c
left join bookings b on b.date = c.date
group by c.date
You can try joining all the combinations of IDs and dates and left joining the data;
WITH Booking AS (SELECT *
FROM (VALUES
( 1 , '06/01/2016', 2 )
, ( 1 , '06/02/2016', 1 )
, ( 2 , '06/03/2016', 2 )
, ( 3 , '06/03/2016', 4 )
) x (id, date, hours)
)
, lookupid AS (
SELECT DISTINCT id FROM Booking
)
, lookupCalender AS (
SELECT DISTINCT date FROM Booking
)
SELECT ID.id, Cal.Date, ISNULL(B.Hours,0) AS hours
FROM lookupid id
INNER JOIN lookupCalender Cal
ON 1 = 1
LEFT JOIN Booking B
ON id.id = B.id
AND Cal.date = B.Date
ORDER BY ID.id, Cal.Date

SQL:Query to check if a column meets certain criteria, if it does perform one action if it doesn't perform another

I have found it quite hard to word what I want to do in the title so I will try my best to explain now!
I have two tables which I am using:
Master_Tab and Parts_Tab
Parts_Tab has the following information:
Order_Number | Completed| Part_Number|
| 1 | Y | 64 |
| 2 | N | 32 |
| 3 | Y | 42 |
| 1 | N | 32 |
| 1 | N | 5 |
Master_Tab has the following information:
Order_Number|
1 |
2 |
3 |
4 |
5 |
I want to generate a query which will return ALL of the Order_Numbers listed in the Master_Tab on the following conditions...
For each Order_Number I want to check the Parts_Tab table to see if there are any parts which aren't complete (Completed = 'N'). For each Order_Number I then want to count the number of uncompleted parts an order has against it. If an Order_Number does not have uncompleted parts or it is not in the Parts_Table then I want the count value to be 0.
So the table that would be generated would look like this:
Order_Number | Count_of_Non_Complete_Parts|
1 | 2 |
2 | 1 |
3 | 0 |
4 | 0 |
5 | 0 |
I was hoping that using a different kind of join on the tables would do this but I am clearly missing the trick!
Any help is much appreciated!
Thanks.
I have used COALESCE to convert NULL to zero where necessary. Depending on your database platform, you may need to use another method, e.g. ISNULL or CASE.
select mt.Order_Number,
coalesce(ptc.Count, 0) as Count_of_Non_Complete_Parts
from Master_Tab mt
left outer join (
select Order_Number, count(*) as Count
from Parts_Tab
where Completed = 'N'
group by Order_Number
) ptc on mt.Order_Number = ptc.Order_Number
order by mt.Order_Number
You are looking for a LEFT JOIN.
SELECT mt.order_number, count(part_number) AS count_noncomplete_parts
FROM master_tab mt LEFT JOIN parts_tab pt
ON mt.order_number=pt.order_number AND pt.completed='N'
GROUP BY mt.order_number;
It is also possible to put pt.completed='N' into a WHERE clause, but you have to be careful of NULLs. Instead of the AND you can have
WHERE pt.completed='N' OR pr.completed IS NULL
SELECT mt.Order_Number SUM(tbl.Incomplete) Count_of_Non_Complete_Parts
FROM Master_Tab mt
LEFT JOIN (
SELECT Order_Number, CASE WHEN Completed = 'N' THEN 1 ELSE 0 END Incomplete
FROM Parts_Tab
) tbl on mt.Order_Number = tbl.Order_Number
GROUP BY mt.Order_Number
Add a WHERE clause to the outer query if you need to filter for specific order numbers.
I think it's easiest to get a subquery in there. I think this should be self-explanitory, if not feel free to ask any questions.
CREATE TABLE #Parts
(
Order_Number int,
Completed char(1),
Part_Number int
)
CREATE TABLE #Master
(
Order_Number int
)
INSERT INTO #Parts
SELECT 1, 'Y', 64 UNION ALL
SELECT 2, 'N', 32 UNION ALL
SELECT 3, 'Y', 42 UNION ALL
SELECT 1, 'N', 32 UNION ALL
SELECT 1, 'N', 5
INSERT INTO #Master
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
SELECT 6
SELECT M.Order_Number, ISNULL(Totals.NonCompletedCount, 0) FROM #Master M
LEFT JOIN (SELECT P.Order_Number, COUNT(*) AS NonCompletedCount FROM #Parts P
WHERE P.Completed = 'N'
GROUP BY P.Order_Number) Totals ON Totals.Order_Number = M.Order_Number