I have a table with columns mentioned below:
transaction_type transaction_number amount
Sale 2016040433 50
Cancel R2016040433 -50
Sale 2016040434 50
Sale 2016040435 50
Cancel R2016040435 -50
Sale 2016040436 50
I want to find net number of rows with only sales which does not include canceled rows.
(Using SQL Only).
If you just want to count the sales and subtract the cancels (as suggested by your sample data), you can use conditional aggregation:
select sum(case when transaction_type = 'Sale' then 1
when transaction_type = 'Cancel' then -1
else 0
end)
from t;
SELECT count(transaction_type) FROM TBL_NAME GROUP BY count(transaction_type) HAVING transaction_type = 'Sale'
Just use the count function, and use HAVING with GROUP BY to filter
The list of row
select transaction_number, amount
from table
where transaction_type ='Sale';
or sum
select sum(amount)
from table
where transaction_type ='Sale';
or count
select count(*)
from table
where transaction_type ='Sale';
Since you have the transaction number, which i assume is a unique identifier for each transaction, i would use it to count the net sale rows.
Assuming then than every Cancelled transaction number is 'R'+ Sales transact number, then the query i propose is the following:
WITH CancelledSales_CTE (transaction_type, transaction_number, amount)
AS
(
SELECT transaction_type, RIGHT(transaction_number, LEN(transaction_number) - 1), amount
FROM Sales
WHERE transaction_type IS 'Cancel'
)
SELECT count(transaction_number)
FROM Sales
where transaction_number not in
(select distinct transaction_number from CancelledSales_CTE )
CancelledSales_CTE contains the list of cancelled rows, with the transaction number of the relative sale.
The count coming from
SELECT count(transaction_number)
FROM Sales
where transaction_number not in
(select distinct transaction_number from CancelledSales_CTE )
will exclude the transaction numbers which have been canceled
Related
I have tried following this and this(SQL Server specific solution) but were not helpful.
I have two tables, Product and Sale and I want to find how many products are sold on each day. But I want to pivot the table so that columns become the products name and each row will contain the amount of products sold for each day ordered by the day.
Simplified schema is as following
CREATE TABLE product (
id integer,
name varchar(40),
price float(2)
);
CREATE TABLE sale(
id integer,
product_id integer,
transaction_time timestamp
);
This is what I want
I only managed to aggregate the total sales per day per product but I am not able to pivot the product names.
select date(sale.transaction_date)
, product.id
, product.name
, count(product_id)
from sale inner join
product on sale.product_id = product.id
group by date(sale.transaction_date)
, product.id
, product.name
This is the situation so far
Please suggest.
You need pivoting logic, e.g.
select
s.transaction_date::date,
count(case when p.name = 'intelligent_rubber_clock' then 1 end) as intelligent_rubber_clock,
count(case when p.name = 'intelligent_iron_wallet' then 1 end) as intelligent_iron_wallet,
count(case when p.name = 'practical_marble_car' then 1 end) as practical_marble_car
from sale s
inner join product p
on s.product_id = p.id
group by
s.transaction_date::date;
Since your expected output aggregates by date alone, then only the transaction date should be in your GROUP BY clause. The trick used here is to take the count of a CASE expression which returns 1 when the record is from a given product, and 0 otherwise. This generates conditional counts for each product, all in separate columns. To add more columns, just add more conditional counts.
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.
I'm running PostgreSQL 9.4 and have the following table structure for invoicing:
id BIGINT, time UNIX_TIMESTAMP, customer TEXT, amount BIGINT, status TEXT, billing_id TEXT
I hope I can explain my challenge correctly.
A invoice record can have 2 different status; begin, ongoing and done.
Several invoice records can be part of the same invoice line, over time.
So when an invoice period begins, a record is started with status begin.
Then every 6 hour there will be generated a new record with status ongoing containing the current amount spend in amount.
When an invoice is closed a record with status done is generated with the total amount spend in column amount. All the invoice records within the same invoice contains the same billing_id.
To calcuate a customers current spendings I can run the following:
SELECT sum(amount) FROM invoice_records where id = $1 and time between '2017-06-01' and '2017-07-01' and status = 'done'
But that does not take into account if there's an ongoing invoice which are not closed yet.
How can I also count the largest billing_id with no status done?
Hope it make sense.
Per invoice (i.e. billing_id) you want the amount of the record with status = 'done' if such exists or of the last record with status = 'ongoing'. You can use PostgreSQL's DISTINCT ON for this (or use standard SQL's ROW_NUMBER to rank the records per invoice).
SELECT DISTINCT ON (billing_id) billing_id, amount
FROM invoice_records
WHERE status IN ('done', 'ongoing', 'begin')
ORDER BY
billing_id,
CASE status WHEN 'done' THEN 1 WHEN 'ongoing' THEN 2 ELSE 3 END,
unix_timestamp desc;
The ORDER BY clause represents the ranking.
select sum (amount), id
from (
select distinct on (billing_id) *
from (
select distinct on (status, billing_id) *
from invoice_records
where
id = $1
and time between '2017-06-01' and '2017-07-01'
and status in ('done', 'ongoing')
order by status, billing_id desc
) s
order by billing_id desc
) s
I am trying to get a summary of the balance per month within my database. The table has the following fields
tran_date
type (Income or Expense)
amount
I can get as far as retrieving the sum for each type for every month but want the sum for the whole month. This is my current query:
SELECT DISTINCT strftime('%m%Y', tran_date), type, SUM(amount) FROM tran WHERE exclude = 0 GROUP BY tran_date, type
This returns
032013 Income 100
032013 Expense 200
I would like the summary on one row, in this example 032013 -100.
Just use the right group by. This uses conditional aggregation, assuming that you want "income - expense":
SELECT strftime('%m%Y', tran_date), type,
SUM(case when type = 'Income' then amount when type = 'Expense' then - amount end)
FROM tran WHERE exclude = 0
GROUP BY tran_date;
If you want just the full sum, then this is easier:
SELECT strftime('%m%Y', tran_date), type,
SUM(amount)
FROM tran WHERE exclude = 0
GROUP BY tran_date;
Your original query returned type rows because "type" was in the group by clause.
Also, distinct is (almost) never needed with group by.
A friend has suggested I post here as I'm in need of a bit of help!
DB Layout:
**salestable**
salesorder [primary, unique] (sales order number)
salesman (salesperson id)
**salesline**
salesorder [many sales line to single salestable relationship]
saleprice (line amount)
isaccessory (0 or 1)
I'd like to, in a single select, sum sales price as a total per salesman, but also sum sales price where is accessory = 1.
The grouping and joins aren't what I'm stuck on, it's whether I can do the dual sum.
In my head it would look something like
...salesman,sum(amount) as totalsales,sum(amount where accessory=1) as accessorysales...
But obviously that would never work!
Thanks in advance! (PS, I'm very new here, be gentle...)
SELECT salesman, SUM(amount), SUM(CASE WHEN accessory = 1 THEN amount ELSE 0 END)
FROM salestable t
JOIN salesorder o
ON o.salesorder = t.salesorder
GROUP BY
salesman