Retrieve the total number of orders made and the number of orders for which payment has been done - sql

Retrieve the total number of orders made and the number of orders for which payment has been done(delivered).
TABLE ORDER
------------------------------------------------------
ORDERID QUOTATIONID STATUS
----------------------------------------------------
Q1001 Q1002 Delivered
O1002 Q1006 Ordered
O1003 Q1003 Delivered
O1004 Q1006 Delivered
O1005 Q1002 Delivered
O1006 Q1008 Delivered
O1007 Q1009 Ordered
O1008 Q1013 Ordered
Unable to get the total number of orderid i.e 8
select count(orderid) as "TOTALORDERSCOUNT",count(Status) as "PAIDORDERSCOUNT"
from orders
where status ='Delivered'
The expected output is
TOTALORDERDSCOUNT PAIDORDERSCOUNT
8 5

I think you want conditional aggregation:
select count(*) as TOTALORDERSCOUNT,
sum(case when status = 'Delivered' then 1 else 0 end) as PAIDORDERSCOUNT
from orders;

Try this-
SELECT COUNT(ORDERID) TOTALORDERDSCOUNT,
SUM(CASE WHEN STATUS = 'Delivered' THEN 1 ELSE 0 END ) PAIDORDERSCOUNT
FROM ORDER
You can also use COUNT in place of SUM as below-
SELECT COUNT(ORDERID) TOTALORDERDSCOUNT,
COUNT(CASE WHEN STATUS = 'Delivered' THEN 1 ELSE NULL END ) PAIDORDERSCOUNT
FROM ORDER

you could use cross join between the two count
select count(orderid) as TOTALORDERSCOUNT, t.PAIDORDERSCOUNT
from orders
cross join (
select count(Status) PAIDORDERSCOUNT
from orders where Status ='Delivered'
) t

What I've used in the past for summarizing totals is
SELECT
count(*) 'Total Orders',
sum( iif( orders.STATUS = 'Delivered', 1, 0 ) ) 'Total Paid Orders'
FROM orders
I personally don't like using CASE WHEN if I don't have to. This logic may look like its a little too much for a simple summation of totals, but it allows for more conditions to be added quite easily and also just involves less typing, at least for what I use this regularly for.
Using the iif( statement to set up the conditional where you're looking for all rows in the STATUS column with the value 'Delivered', with this set up, if the status is 'Delivered', then it marks it stores a value of 1 for that order, and if the status is either 'Ordered' or any other value, including null values or if you ever need a criteria such as 'Pending', it would still give an accurate count.
Then, nesting this within the 'sum' function totals all of the 1's denoted from your matched values. I use this method regularly for report querying when there's a need for many conditions to be narrowed down to a summed value. This also opens up a lot of options in the case you need to join tables in your FROM statement.
Also just out of personal preference and depending on which SQL environment you're using this in, I tend to only use AS statements for renaming when absolutely necessary and instead just denote the column name with a single quoted string. Does the same thing, but that's just personal preference.
As stated before, this may seem like it's doing too much, but for me, good SQL allows for easy change to conditions without having to rewrite an entire query.
EDIT** I forgot to mention using count(*) only works if the orderid's are all unique values. Generally speaking for an orders table, orderid is an expected unique value, but just wanted to add that in as a side note.

SELECT DISTINCT COUNT(ORDERID) AS [TOTALORDERSCOUNT],
COUNT(CASE WHEN STATUS = 'ORDERED' THEN ORDERID ELSE NULL END) AS [PAIDORDERCOUNT]
FROM ORDERS
TotalOrdersCount will count all distinct values in orderID while the case statement on PaidOrderCount will filter out any that do not have the desired Status.

Related

TSQL "where ... group by ..." issue that needs solution like "having ..."

I have 3 sub-tables of different formats joined together with unions if this affects anything into full-table. There I have columns "location", "amount" and "time". Then to keep generality for my later needs I union full-table with location-table that has all possible "location" values and other fields are null into master-table.
I query master-table,
select location, sum(amount)
from master-table
where (time...)
group by location
However some "location" values are dropped because sum(amount) is 0 for those "location"s but I really want to have full list of those "location"s for my further steps.
Alternative would be to use HAVING clause but from what I understand HAVING is impossible here because i filter on "time" while grouping on "location" and I would need to add "time" in grouping which destroys the purpose. Keep in mind that the goal here is to get sum(amount) in each "location"
select location, sum(amount)
from master-table
group by location, time
having (time...)
To view the output:
with the first code I get
loc1, 5
loc3, 10
loc6, 1
but I want to get
loc1, 5
loc2, 0
loc3, 10
loc4, 0
loc5, 0
loc6, 1
Any suggestions on what can be done with this structure of master-table? Alternative solution to which I have no idea how to code would be to add numbers from the first query result to location-table (as a query, not actual table) with the final result query that I've posted above.
What you want will require a complete list of locations, then a left-outer join using that table and your calculated values, and IsNull (for tsql) to ensure you see the 0s you expect. You can do this with some CTEs, which I find valuable for clarity during development, or you can work on "putting it all together" in a more traditional SELECT...FROM... statement. The CTE approach might look like this:
WITH loc AS (
SELECT DISTINCT LocationID
FROM location_table
), summary_data as (
SELECT LocationID, SUM(amount) AS location_sum
FROM master-table
GROUP BY LocationID
)
SELECT loc.LocationID, IsNull(location_sum,0) AS location_sum
FROM loc
LEFT OUTER JOIN summary_data ON loc.LocationID = summary_data.LocationID
See if that gets you a step or two closer to the results you're looking for.
I can think of 2 options:
You could move the WHERE to a CASE WHEN construction:
-- Option 1
select
location,
sum(CASE WHEN time <'16:00' THEN amount ELSE 0 END)
from master_table
group by location
Or you could JOIN with the possible values of location (which is my first ever RIGHT JOIN in a very long time 😉):
-- Option 2
select
x.location,
sum(CASE WHEN m.time <'16:00' THEN m.amount ELSE 0 END)
from master_table m
right join (select distinct location from master_table) x ON x.location = m.location
group by x.location
see: DBFIDDLE
The version using T-SQL without CTEs would be:
SELECT l.location ,
ISNULL(m.location_sum, 0) as location_sum
FROM master-table l
LEFT JOIN (
SELECT location,
SUM(amount) as location_sum
FROM master-table
WHERE (time ... )
GROUP BY location
) m ON l.location = m.location
This assumes that you still have your initial UNION in place that ensures that master-table has all possible locations included.
It is the where clause that excludes some locations. To ensure you retain every location you could introduce "conditional aggregation" instead of using the where clause: e.g.
select location, sum(case when (time...) then amount else 0 end) as location_sum
from master-table
group by location
i.e. instead of excluding some rows from the result, place the conditions inside the sum function that equate to the conditions you would have used in the where clause. If those conditions are true, then it will aggregate the amount, but if the conditions evaluate to false then 0 is summed, but the location is retained in the result.

Combining Count and MIN functions

I have a part of my query as:
SUM(POReceiptQuantity) as Receieved,
MIN(ItemLocalStandardCost) as Low,
MAX(ItemLocalStandardCost) as High,
Received returns the total number of Items we sold this year. The LOW is the lowest price we paid, and High is the highest price we paid.
I'm trying to incorporate a new column showing how many if the item we sold at the Low price. I tried to use Count along with Min function but it returns a "cannot perform an aggregate function on an expression containing an aggregate or a subquery"
Does anyone have any ideas how i could go about this.
Thank you
You need create a subquery with your current GROUP BY query and join with your Original Table. Then you can use a conditional COUNT
SELECT T2.Received,
T2.Low,
COUNT( CASE WHEN T1.ItemLocalStandardCost = T2.Low THEN 1 END) as Total_Low,
T2.High,
COUNT( CASE WHEN T1.ItemLocalStandardCost = T2.High THEN 1 END) as Total_High
FROM YourTable T1
CROSS JOIN ( SELECT SUM(Y.POReceiptQuantity) as Receieved,
MIN(Y.ItemLocalStandardCost) as Low,
MAX(Y.ItemLocalStandardCost) as High
FROM YourTable Y
GROUP BY .... ) as T2

Dynamic column in Oracle SQL select

I have three tables:
T_ORDER_PLACEMENTS (ORDER_ID, CUSTOMER_ID, ORDER_DATE)
T_ORDER_DETAILS (ORDER_ID, STOCK_ID)
T_STOCK_DETAILS(STOCK_ID, STOCK_NAME, STOCK_PRICE)
Can someone please help me to write a query which generates the following output:
STOCK_ID, STOCK_NAME, STOCK_PRICE, ORDERED_STATUS
1 stock1 5000 ordered
2 stock2 10000 unordered
Populate the ORDERED_STATUS column with 'ordered' if the stock is ordered and 'unordered' if the stock is unordered.
SELECT
t_stock_details.*,
CASE WHEN order_check.stock_id IS NULL THEN 'unordered' ELSE 'ordered' END AS ordered_status
FROM
t_stock_details
LEFT JOIN
(
SELECT stock_id FROM t_order_details GROUP BY stock_id
)
order_check
ON order_check.stock_id = t_stock_details.stock_id
The sub-query checks to see which stock_ids have an order associated with them. It also uses GROUP BY to ensure only one row is returned per stock_id, no matter how many orders are found.
The LEFT JOIN ensures that every row in t_stock_details is returned, whether or not it is successfully joined to anything. Where there is a successful join, we know there has been an order. It will also only ever be joined on to one row at the most (thanks to the above mentioned GROUP BY, so no duplication is being caused).
An unsuccessful join will have NULL in the order_check.stock_id, so we use that to check which string to return, using a CASE statement.

SQLite3 database conditional summing

I am looking to order a list of keys based on the number of orders placed from a database containing order requests. Basically, on table, call it orders(o_partkey, o_returnflag) I am trying to get the total number of returns for each order. I have tried many variations of the following snippet with the goal schema returnlist(partkey, numreturns):
select O.o_partkey as partkey,
count(case when O.o_returnflag = 'R' then 1 else 0 end) as numreturns
from orders O
orderby quantity_returned desc;
I am very new to SQLite and am just jumping into the basics. This is an adjustment of a homework question (the actual question is more complex) but I have simplified down the issue I am having.
Consider using a derived table subquery with SUM() as the aggregate function:
SELECT dT.partkey, dT.numreturns
FROM
(SELECT O.o_partkey as partkey,
SUM(CASE WHEN O.o_returnflag = 'R' THEN 1 ELSE 0 END) as numreturns
FROM [ORDER] O
GROUP BY O.o_partkey) AS dT
ORDER BY dT.numreturns DESC;
Be sure to bracket name of table as [ORDER] is an SQLite key word.
Your problem is that COUNT counts rows, so it counts both 0 and 1 values.
You are not interested in any other rows, so you can just filter out the returns with WHERE:
SELECT o_partkey AS partkey,
COUNT(*) AS numreturns
FROM orders
WHERE o_returnflag = 'R'
ORDER BY 2 DESC;

How to write this SQL Order By Clause

I have a SQL Query which I am trying to write and am now a bit stuck on how to write the order by clause.
Basically the table I am selecting from has items with a severity value. I want to select these items and order them so that the Severity column is ordered Severity 1-4 then 0 and the log date is descending for each.
Severity 1 is highest 4 is lowest and 0 respresents an unassigned severity, I need to display these items Highest severity, oldest item first, lowest severity, newest item last.
My query so far:
SELECT
[TicketID],
[type],
[Product],
[Description],
[LoggedBy],
[LogDate],
[Department],
[AssignedTo],
[Severity],
[Status],
[LastUpdatedBy],
[LastUpdatedDate]
FROM
SupportTicketsTbl
WHERE
TicketID NOT IN
(
SELECT
tck.ticketID
FROM
SupportTicketsTbl tck
JOIN
tblTicketsInEvents tie
ON
tck.TicketID = tie.ticketID
JOIN
tblSupportEvent ev
ON
tie.eventID = ev.id
where
ev.type = 1
)
AND
Status <> 'Complete'
I guess the easiest way is to create a table variable and select all the Items that are not 0 into it in the order I want, then select all the 0 items into my table variable, and finally just select everything back out of the table variable, but this seems a bit messy so im wondering if there is a more elegant solution?
Thanks
Since you didn't like the UNION answer, and I'm not sure if UNION is guaranteed to preserve order...
ORDER BY CASE WHEN severity = 0 THEN 999 ELSE Severity END, date
You can order by a case statement like this:
ORDER BY CASE Severity WHEN 0 THEN 1 ELSE 2 END, Severity
First, Select all of the ones with severity levels 1-4 using a standard orderby clause, then union the results with a second query that selects only the ones with severity level 0.
Unless I'm very much mistaken, it's something like this:
ORDER BY severity DESC, date DESC
Insert that line into your SQL.
This will sort the data by Severity first, and if they have the same severity, then sort it according to date.