how to do an sql statement like this one that actually works:
select distinct store_id as myid,
(select count(*) from products where store_id = myid and delete_flag = true)
from products
where delete_flag = true
I want a column with every store id and the number of deleted products with this store id.
select store_id,
count(*) as all_product_count,
sum(case when delete_flag = 1 then 1 else 0 end) as deleted_product_count
from products
group by store_id
Hope it helps!
select store_id as myid , count(*) as noofdeleteditems from products where delete_flag = true group by store_id;
Related
SELECT *
FROM
(SELECT *
FROM salestotal) x
PIVOT
(SUM(sales)
FOR product_id IN ([1256], [4437], [7684])
) p
This the code that I tried. SQL fiddle says that something wrong with the code.
the table for reference
the fiddle
To get the sum(sales) per product_id :
SELECT product_id, sum(sales)
FROM sales_totals
GROUP BY product_id
And joining with the product table columns :
SELECT p.*, s.total_sales
FROM
( SELECT product_id, sum(sales) AS total_sales
FROM sales_totals
GROUP BY product_id
) AS s
INNER JOIN product AS p
ON s.product_id = p.product_id
What do you need in addition ?
Just an FYI, PIVOT function doesn't exist in Postgres. You may try to use crosstab function of tablefunc module, which provides the same functionality.
Below are two other ways of doing it.
SELECT SUM(CASE WHEN product_id = 1256 THEN sales ELSE 0 END) AS "1256",
SUM(CASE WHEN product_id = 4437 THEN sales ELSE 0 END) AS "4437",
SUM(CASE WHEN product_id = 7684 THEN sales ELSE 0 END) AS "7684"
FROM sales_totals
SELECT SUM(sales) FILTER (WHERE product_id = 1256) AS "1256",
SUM(sales) FILTER (WHERE product_id = 4437) AS "4437",
SUM(sales) FILTER (WHERE product_id = 7684) AS "7684"
FROM sales_totals
I have a table with 4 columns.
date
store_id
product_id
label_id
and I need to find all store_ids that have all products_id with same label_id (for example 4)in one day.
for example:
store_id | label_id | product_id | data|
4 4 5 9/2
5 4 7 9/2
4 3 12 9/2
4 4 7 9/2
so it should return 4 because it's the only store that contains all possible products with label 4 at one day.
I have tried something like this:
(select store_id, date
from table
where label_id = 4
group by store_id, date
order by date)
I dont know how to write the outer query, I tried:
select * from table
where product_id = all(Inner query)
but it didnt work.
Thanks
It is unclear from your question whether the labels are specific to a given day or through the entire period. But a variation of Tim's answer seems appropriate. For any label:
SELECT t.date, t.label, t.store_id
FROM t
GROUP BY t.date, t.label, t.store_id
HAVING COUNT(DISTINCT t.product_id) = (SELECT COUNT(DISTINCT t2product_id)
FROM t t2
WHERE t2.label = t.label
);
For a particular label:
SELECT t.date, t.store_id
FROM t
WHERE t.label = 4
GROUP BY t.date,t.store_id
HAVING COUNT(DISTINCT t.product_id) = (SELECT COUNT(DISTINCT t2product_id)
FROM t t2
WHERE t2.label = t.label
);
If the labels are specific to the date, then you need that comparison in the outer queries as well.
Here is one way:
SELECT date, store_id
FROM yourTable
GROUP BY date, store_id
HAVING COUNT(DISTINCT product_id) = (SELECT COUNT(DISTINCT product_id)
FROM yourTable t2
WHERE t2.date = t1.date)
ORDER BY date, product_id;
This query reads in a pretty straightforward way, and it says to find every product, on some date, whose distinct product count is the same as the distinct product count on the same day, across all stores.
I'd probably aggregate to lists of products in a string or array:
with products_per_day_and_store as
(
select
store_id,
date,
string_agg(distinct product_id order by product_id) as products
from mytable
where label_id = 4
group by store_id, date
)
, products_per_day
(
select
date,
string_agg(distinct product_id order by product_id) as products
from mytable
where label_id = 4
group by date
)
select distinct ppdas.store_id
from products_per_day_and_store ppdas
join products_per_day ppd using (date, products);
I have a big query creating a history of object changes. In short it result looks like this:
id changedOn recordtype
1 2019-12-5 history
1 2020-01-1 history
1 2020-01-7 actual
2 2018-10-9 history
The result I want:
id changedOn recordtype
1 2019-12-5 history
1 2020-01-7 actual
2 2018-10-9 history
If there is 2 records in the same month on each id I want to ommit history records for this Month.
I would like to avoid cursor if it possible. But I'm stuck.
If you want one record per month with a preference for "actual", then use row_number():
select t.*
from (select t.*,
row_number() over (partition by id, year(changedOn), month(changedOn) order by recordtype) as seqnum
from t
) t
where seqnum = 1;
If you want all "actual" records for a month -- and then if there are none -- all the history records, I would recommend logic like this:
select t.*
from t
where t.recordtype = 'actual' or
(t.recordtype = 'history' and
not exists (select 1
from t t2
where t2.id = t.id and
t2.recordtype = 'actual' and
year(t2.changedon) = year(t.changedon) and
month(t2.changedon) = month(t.changedon)
);
These two approaches are subtly different. But you will only notice the differences if you have multiple "actual"s or "history"s in a single month for a single id.
Just remove the records with changedOn that are not the most recent
select * from tbl a
where not exists
(select 1 from tbl b where a.id = b.id and a.recordtype = b.recordtype and a.changedOn < b.changedOn )
Below is a list of orders, is there a way to find the person_id of the customers, that has only bought products no one else has bought?
CREATE TABLE orders
AS
SELECT product_id, person_id
FROM ( VALUES
( 1 , 1 ),
( 2 , 1 ),
( 2 , 2 ),
( 3 , 3 ),
( 12, 6 ),
( 10, 3 )
) AS t(product_id, person_id);
The result would be the following table:
| person_id |
|-----------|
| 3 |
| 6 |
Do i have to find all the people who did buy items no one else bought and create a table that doesn't include those people?
You want all the products purchased by the person to be unique.
select person_id
from (select t.*,
min(person_id) over (partition by product_id) as minp,
max(person_id) over (partition by product_id) as maxp
from t
) t
group by person_id
having sum(case when minp <> maxp then 1 else 0 end) = 0;
You are probably thinking "Huh? What does this do?".
The subquery calculates the minimum person and maximum person on each product. If these are the same, than that one person is the only purchaser.
The having then checks that there are no non-single-purchaser products for a given person.
Perhaps a more intuitive phrasing of the logic would be:
select person_id
from (select t.*,
count(distinct person_id) over (partition by product_id) as numpersons
from t
) t
group by person_id
having max(numperson) = 1;
Alas, Postgres doesn't support COUNT(DISTINCT) as a window function.
The traditional self join with boolean aggregation
select o0.person_id
from
orders o0
left join
orders o1 on o0.product_id = o1.product_id and o0.person_id <> o1.person_id
group by o0.person_id
having bool_and(o1.product_id is null)
;
person_id
-----------
3
6
The inline view which is being joined gets all the product_ids which have only one person_id. Once all product_ids are found they will be joined to the original customers table to get the person_ids. This should solve your problem!!
SELECT person_id
FROM customers c1
INNER JOIN
(
SELECT product_id
FROM customers
GROUP BY product_id
HAVING COUNT(person_id ) = 1
) c2
ON c1.product_id = c2.product_id;
This is Gordon's logic using aggregates only:
SELECT person_id
FROM
(
SELECT product_id,
-- if count = 1 it's the only customer who bought this product
min(person_id) as person_id,
-- if the combination(person_id,product_id) is unique DISTINCT can be removed
count(distinct person_id) as cnt
FROM customers
GROUP BY product_id
) AS dt
GROUP BY person_id
HAVING max(cnt) = 1 -- only unique products
Here is another solution:
with unique_products as
(select product_id
from orders
group by product_id
having count(*) = 1)
select person_id
from orders
except
select person_id
from orders
where not exists
(select * from unique_products where unique_products.product_id = orders.product_id)
First all the identifier of products that appear in a single order are found. Then we subtract from all the persons (in the orders) those which do not have a order with a single product (i.e. all the persons that have at least ordered a product ordered by somebody else).
I have a database view which gives me following result:
From this I want to select all the users which have groups 00113 and 00221.. so on. That is the users which have access to all those groups and not any one group.
I am a fan of using group by and having for these types of queries. One method for finding things in a list:
select id
from table
where group_number in ('00113', '00221')
group by id
having count(distinct group_number = '00113') = 2;
A more general method that allows you to get members of one list, excluding members from another:
select id
from table
group by id
having sum(case when group_number = '00113' then 1 else 0 end) > 0 and
sum(case when group_number = '00221' then 1 else 0 end) > 0;
select * from table
where ID not IN
( select distinct ID from Table
where GROUP_NUMBER not IN ('00221','00113')
) group by ID having count (distinct GROUP_NUMBER) = 2;
As ruudvan pointed out this can be directly done with only one IN
select * from table
where ID IN ('00221','00113')
group by ID having count (distinct GROUP_NUMBER) = 2;
The inner query gives the list of all IDs whose group_number is not present in the given list.
So you need to select all the IDs not present in the IDs given by inner query
Your intent is a bit ambiguous but if what you want is to return the user that for example have access to the groups 00113 and 00221 and no other groups then this would work.
One way to do this would be to use a conditional aggregation in the having clause of the group by:
select id from your_table
group by id
having sum(case
when group_number = '00113' then 1
when group_number = '00221' then 1
else -1 end
) = 2
Sample SQL Fiddle
select first_name, last_name, id from mytable where group_number = '00113'
INTERSECT
select first_name, last_name, id from mytable where group_number = '00221'