SQL: Select count of accomplished invoices with all items checked - sql

My customers submit invoices with some items in each one. I want to calculate number of accomplished invoices (which all items are checked by operator)
sample data:
invoiceNumber | ItemNumber | Status
a 1 Null
a 2 checked
a 3 Null
b 1 checked
b 5 checked
In sample data above, the number of finished invoice is 1 because all items in invoice number "B" are checked and number of unfinished invoices is 1 because in invoice "A", only 1 item is checked.
My try:
select count(distinct invoiceNumber) as total
from invoices
where status is not null
Returns 2! I should not count row number 2 because 1 and 3 are still Null.

The distinct is the problem as you count the unique appearances of the invoiceNumber as the result. As there are two bs checked and one a, the count is 2
Try using the select count (*) instead or some unique id of the invoice (if there is one).
Edit:
I have misread your question. To count only the invoices that have all the rows with the status checked, you can use the group by and having.
Something similar to:
select count(distinct invoiceNumber) as total
from invoices
group by invoiceNumber, status
having status is not null

use the below query..
SELECT count(distinct invoiceNumber) as total
FROM from invoices
WHERE invoiceNumber NOT IN (SELECT invoiceNumber
FROM invoices WHERE status IS null)

You need to exclude all invoices that have a NULL status for the same invoicenumber:
select count(distinct i1.invoicenumber)
from invoices i1
where not exists (select *
from invoices i2
where i2.invoicenumber = i1.invoicenumber
and i2.status is null);
Another option is to use except to remove those that have a null status:
select count(*)
from (
select invoicenumber
from invoices
except
select invoicenumber
from invoices
where status is null
);

Related

SQL - Filter data where number of lines is variable

I have invoice data (sample below) where each invoice has one or more lines and each line has one or more sequential assigned reviewers.
I want to filter the data to return only invoices where all lines have been approved by all reviewers.
So in the sample below I want 123 to return, but not 234 because invoice line 2 is still pending for reviewer 2. I am fairly new to SQL and at a loss on how to do this.
InvoiceNo.
InvoiceLine
ApprovalSeq
Status
123
1
1
Approved
123
1
2
Approved
123
2
1
Approved
234
1
1
Approved
234
1
2
Approved
234
2
1
Approved
234
2
2
Pending
One method for this is to use conditional aggregation:
select invoiceno
from t
group by invoiceno
having count(*) = sum(case when status = 'Approved' then 1 else 0 end);
Actually, because 'Approved' may be the minimum status alphabetically, you can use:
select invoiceno
from t
group by invoiceno
having max(status) = 'Approved'
you can use case statement to have conditional count.
select invoiceno
from invoice
group by invoiceno
having count(*) = count(case when status = 'Approved' then 1 else null end)
You could do that using sub-query as one of the options:
SELECT DISTINCT InvoiceNo FROM TABLE x LEFT JOIN
(SELECT DISTINCT InvoiceNo FROM TABLE x1 WHERE Status != 'Approved') notAppr
ON x.InvoiceNo = notAppr.InvoiceNo
WHERE notAppr.InvoiceNo IS NULL
It has been shown how to get the invoice numbers where no approval is pending by aggreating your invoice line approval table. You probably want to use this to select from the invoice table:
select *
from invoice
where invoiceno in
(select invoiceno from invoice_line_approval group by invoiceno having ...)
If so, you don't have to aggregate the whole invoice line approval table, but merely check whether a non-approved row exists for an invoice with NOT EXISTS:
select *
from invoice i
where not exists
(
select null
from invoice_line_approval ila
where ila.invoiceno = i.invoiceno
and ila.status <> 'Approved'
);
(If the status can be null, then you need and (ila.status <> 'Approved' or ila.status is null), though.)

Get total count and filtered count from single subquery

product table
product_id
name
1
t-shirt
2
shirt
users table
user_id
name
1
sphinx
favorite table with a UNIQUE constraint on (user_id, product_id):
user_id
product_id
1
2
What is best way to query this table for user_id = 1
favorite_count: how many users added this product in favorite?
isfavorite: did user with user_id = 1 add this product as favorite?
Expected result:
product_id
product_name
isfavorite
favorite_count
1
t-shirt
false
0
2
shirt
true
1
Typically, it's cheapest to aggregate rows in the n-table (favorite in this case) before the join:
SELECT p.*
, COALESCE(f.is_favorite, false) AS is_favorite
, COALESCE(f.favorite_count, 0) AS favorite_count
FROM product p
LEFT JOIN (
SELECT product_id
, count(*) AS favorite_count -- total count
, bool_or(true) FILTER (WHERE user_id = 1) AS is_favorite -- for given user
FROM favorite
GROUP BY 1
) f USING (product_id);
We don't need the users table for the query.
LEFT JOIN keeps all products in the result, while only adding counts for products that have favorite entries.
About the FILTER clause:
Aggregate columns with additional (distinct) filters
Since not every product has entries in table favorite, use COALESCE() in the outer query to replace resulting NULL values with your defaults.

SQL: Get count of rows for values in another column even when those values do not exist

I have a table named 'products' with two columns: name and status.
I would like to get the count of rows in the table with statuses as Draft, Published and Rejected.
This is the query I tried,
select count(*), status from products where status in ('Draft', 'Published') group by status;
At the moment the table does not have any row with the status as Published or Rejected.
So the above query just returns one row with status and Draft along with its count
count | status
-------+--------
24 | Draft
However, I would like to the query result with the other statuses as zero.
count | status
-------+--------
24 | Draft
0 | Published
0 | Rejected
How should I write the query so that I get the results as above?
You need a list of the statuses and a left join:
select v.status, count(p.status)
from (values ('Draft'), ('Published'), ('Rejected')
) v(status) left join
products p
on p.status = v.status
group by v.status;

select Query Returns Row Number (Dynamic Number as 1,2,3,4..Sequence) as per Row count

I have table named "Invoice".
ID InvoiceNo invoiceDate main_key
1 100078 10.10.2018 1
2 400058 10.10.2018 1
3 78778 10.10.2018 1
4 78778 10.10.2018 2
5 78778 10.10.2018 1
I need to display data row number in combobox using vb.net, that is, to display only invoice serial number and to add id as valuemember to the combobox.
I tried this
SELECT (
SELECT count(*)
FROM Invoice
WHERE A.ID >= ID
) AS ColumnId
,ID AS ColumnCode
FROM Invoice AS A
WHERE A.main_key = 1
In this table main_key is key to main_table that has buyer and seller data.
While using where condition this query returns wrong sequence of row number.
Please help me guys...Thanks in advance ..!
If you want sequential number within each main_key group, consider:
SELECT (
SELECT count(*)
FROM Invoice
WHERE A.ID >= ID AND A.main_key=main_key
) AS ColumnId
,ID AS ColumnCode
FROM Invoice AS A
WHERE A.main_key = 1;

SQL aggregate rows with same id , specific value in secondary column

I'm looking to filter out rows in the database (PostgreSQL) if one of the values in the status column occurs. The idea is to sum the amount column if the unique reference only has a status equals to 1. The query should not SELECT the reference at all if it has also a status of 2 or any other status for that matter. status refers to the state of the transaction.
Current data table:
reference | amount | status
1 100 1
2 120 1
2 -120 2
3 200 1
3 -200 2
4 450 1
Result:
amount | status
550 1
I've simplified the data example but I think it gives a good idea of what I'm looking for.
I'm unsuccessful in selecting only references that only have status 1.
I've tried sub-queries, using the HAVING clause and other methods without success.
Thanks
Here's a way using not exists to sum all rows where the status is 1 and other rows with the same reference and a non 1 status do not exist.
select sum(amount) from mytable t1
where status = 1
and not exists (
select 1 from mytable t2
where t2.reference = t1.reference
and t2.status <> 1
)
SELECT SUM(amount)
FROM table
WHERE reference NOT IN (
SELECT reference
FROM table
WHERE status<>1
)
The subquery SELECTs all references that must be excluded, then the main query sums everything except them
select sum (amount) as amount
from (
select sum(amount) as amount
from t
group by reference
having not bool_or(status <> 1)
) s;
amount
--------
550
You could use windowed functions to count occurences of status different than 1 per each group:
SELECT SUM(amount) AS amount
FROM (SELECT *,COUNT(*) FILTER(WHERE status<>1) OVER(PARTITION BY reference) cnt
FROM tc) AS sub
WHERE cnt = 0;
Rextester Demo