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

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;

Related

how to fetch data from table based on column value and group by its name and count column value

This is body of the table:
ID STATUS
1 Pending
2 Received
3 NULL
4 NULL
I have four categories of status- Pending, Received, Resolved & Rejected. At first any data is received from my website then this record shown as pending record. So, I want to count the status by their names, like -
STATUS TOTAL
Pending 1
Received 1
Resolved 0
Rejected 0
The problem is that I face, in that table their is no Resolved and Rejected data. So how could I show the output as 0.
You need a table of statuses from which you can outer join to your aggregated results:
with tot as (
select status, Count(*) tot
from t
where status is not null
group by status
)
select s.status, IsNull(t.tot,0) total
from (values ('Pending'),('Received'),('Resolved'),('Rejected'))s(status)
left join tot t on t.status=s.status

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.

Aggregate and count after left join

I am aggregating columns of a table to find the count of unique values. For example, aggregating
the status shows that out of 5 alerts there are 2 in open status and 3 that are closed. The simplified table looks like this:
create table alerts (
id,
status,
owner_id
);
The query below uses grouping sets to aggregate multiple columns at once. This approach works well.
with aggs as (
select status
from alerts
where alerts.owner_id = 'x'
)
select status, count(*)
from aggs
group by grouping sets(
(),
(status)
);
the output at its simplest could look like this:
status | count
--------+-------
| 1
new | 1
However, now I need to aggregate additional columns from another table. This table (shown below) can have zero or more rows associated to the first table (alerts:users 1:N).
create table users (
id,
alert_id,
name
);
I have tried updating the query to use a left join but this approach incorrectly inflates the counts of the alert columns.
with aggs as (
select alerts.status, users.name
from alerts
left join users on alerts.id = users.alert_id
where alerts.owner_id = 'x'
-- and additional filtering by columns in the users table
)
select status, name, count(*)
from aggs
group by grouping sets(
(),
(status),
(name)
);
Below is an example of the incorrect results. Since there are 3 rows in the user table the count
for the status column is now 3 but should be 1.
status | name | count
--------+-------------------------+-------
| | 3
| user1 | 1
| user2 | 1
| user3 | 1
new | | 3
How can I perform this aggregation to include the columns from the table with a many-to-one relationship without inflating the counts? In the future I will likely need to aggregate more columns from other tables with a many-to-one relationship and need a solution that will still work with several left joins. All help is much appreciated.
edit: link to db-fiddle https://www.db-fiddle.com/f/buGD2DuJiqf9LGF9rw5EgT/2
Do you just want to count the number of alerts? If so, use count(distinct):
count(distinct alert_id)
Of course, you need this in aggs, so the select would include:
alerts.id as alert_id

find column which have biggest number of approved transactions sql server

I need to find the Filiyal which have biggest number of approved transactions.
I've try like this:
select max(Filiyal)
from Transactions
where Status = 'Approved'
But it gives me wrong result. Where I'm wrong?
Here is my table:
Filiyal | Status
-------------------------
Bank1 | Approved
Bank2 | Not Approved
Bank1 | Approved
Bank4 | Approved
Your query as written would give you the largest value of Filiyal (alphabetically) from the set of banks that have an approved status. If what you are looking to find is the Filiyal with the most approved records, then you need to have something that counts the records that are approved.
select top 1 Filiyal
from Transactions
where Status = 'Approved'
group by Filiyal
order by COUNT(Filiyal) desc
The group by clause returns a record for each Filiyal and the order by orders them by the number of records for each value that has 'Approved' as a status. The "top 1" will return only the record with the most approved records. In case of a tie though you will get one of them randomly.
You can try with order by desc and top 1:
select top 1 Filiyal
, count(*) as total_approved
from Transactions
where status = 'Approved'
group by Filiyal
order by total_approved desc
P.S. Doesn't cover cases when multiple Filiyals can have the same amount of maximum total_approved.

How to select data based on two tables

I have two tables and want to select only the ones where a certain status is 'complete' in the second table. The most important factor is that all shipments need to be completed.
First Table (orders) includes a unique order id and several cells with customer details. For example
Order_id | Name
1001 | John
1002 | Paula
1003 | Ben
The second table (shipments) has all the items a customer ordered and the status whether they were delivered e.g.
Order_id | Shipment_number | Status
1001 | 8004 | complete
1001 | 8003 | processing
1002 | 8005 | complete
1003 | 8008 | processing
1003 | 8007 | processing
1003 | 8009 | complete
I tried it with the following code but unfortunately the results show all order ids where at least one of the associated shipments is 'complete'.
SELECT
order_id,
name
FROM orders
INNER JOIN shipments ON orders.order_id = shipments.order_id
WHERE
shipments.status = 'complete';
I'm pretty new to SQL and really struggling with this. Thanks in advance :)
SELECT Orders.OrderID
FROM orders
WHERE NOT EXISTS (SELECT OrderID
FROM shipments
WHERE status != 'complete' AND
shipments.OrderID = Orders.OrderID)
You can use nested queries to do this:
Write a query to take a count of the shipments.order_id group by order_id, this gives you the total shipments for each order, select the order_id and the count.
Write a query to take a count of the shipments.order_id group by order_id where shipments.status = 'complete', this gives you the completed shipments for each order, select the order_id and the count.
join the result of 1 and 2 with order on order id where (1.count = 2.count), select the name and the order_id.
You could try this
SELECT order_id FROM <orders>
WHERE order_id NOT IN (SELECT order_id FROM <shipments> WHERE status <> 'complete')
Even though, depending on your system I would consider adding a field in order table (effectively de-normalizing it, but benefits/drawbacks depend on how often you need this information) and update it with current order status.
If you need this information often, I might be worth it.
SELECT ship.order_id, ord.name
FROM shipments as ship
left join orders as ord on
ord.order_id = ship.order_id
where ship.status = 'complete'
Generally, the best way to do this is to have a level at the order itself that indicates whether or not the order is completely shipped.
But, this would work although it might not scale really well. To get it to scale better, you would have to do another join BACK to orders in the subqueries to limit the orders that you are looking through.
select order_id, name from
orders JOIN
(select order_id, count(*) from shipments where status = 'complete' group by order_id
INTERSECT
select order_id, count(*) from shipments group by order_id)
on order_id