I have table like this:
+----+---------+---------+--------+
| id | value_x | created | amount |
+----+---------+---------+--------+
value_x is set of six strings, lets say "one", "two", "three", etc.
I need to create report like this:
+--------------+-------------------------+-------------------+----------------------+
| day_of_month | "one" | "two" | [etc.] |
+--------------+-------------------------+-------------------+----------------------+
| 01-01-2011 | "sum(amount) where value_x = colum name" for this specific day |
+--------------+-------------------------+-------------------+----------------------+
Most obvious solution is:
SELECT SUM(amount), DATE(created) FROM `table_name` WHERE value_x=$some_variable GROUP BY DATE(created)
And loop this query six times with another value for $some_variable in every iteration, but I'm courious if is it possible to do this in single query?
What you're asking is called a "pivot table" and is typically achieved as below. The idea is for each potential value of value_x you either produce a 1 or 0 per row and sum 1's and 0's to get the sum for each value.
SELECT
DATE(created),
SUM(CASE WHEN value_x = 'one' THEN SUM(amount) ELSE 0 END) AS 'one',
SUM(CASE WHEN value_x = 'one' THEN SUM(amount) ELSE 0 END) AS 'two',
SUM(CASE WHEN value_x = 'one' THEN SUM(amount) ELSE 0 END) AS 'three',
etc...
FROM table_name
GROUP BY YEAR(created), MONTH(created), DAY(created)
This will come close:
SELECT
s.day_of_month
,GROUP_CONCAT(CONCAT(s.value_x,':',s.amount) ORDER BY s.value_x ASC) as output
FROM (
SELECT DATE(created) as day_of_month
,value_x
,SUM(amount) as amount
FROM table1
GROUP BY day_of_month, value_x
) s
GROUP BY s.day_of_month
You will need to read the output and look for the value_x prior to the : to place the items in the proper column.
The benefit of this approach over #Michael's approach is that you do not need to know the possible values of field value_x beforehand.
Related
Evening all.
I am trying to create a very basic case statement which buckets three scenarios with a 0 or a 1 in mySQL.
I have three fields found in the reference table link below.
Essentially I am trying to bucket any ProductID that has a StatusCD of 'I' and 'O' as 1, only 'I' as 1 and then anything with only an 'O' result and no corresponding 'I' as a 0. What this data is showing is a product coming into the warehouse ('I') and then exiting the warehouse ('O'). I have other fields which are capturing date differences but ultimately I am trying to create a flag to ignore scenarios where we only have Product#'s with an 'O' statusCD which would indicate their arrival to the warehouse was not logged appropriately and would skew our "Age in warehouse" buckets.
Any insight is greatly appreciated!
Reference Table with fields:
Date | ProductID | StatusCD
2021-01-01 | U1000 | I
2021-01-10 | U1000 | O
2021-01-10 | U2000 | I
2021-01-15 | U3000 | O
Assuming you want to retain every original record, you could use analytic functions here:
SELECT Date, ProductID, StatusCD,
CASE WHEN SUM(StatusCD <> 'O') OVER (PARTITION BY ProductID) = 0
THEN 0 ELSE 1 END As Label
FROM yourTable
ORDER BY Date;
Demo
For versions of MySQL earlier than 8+:
SELECT t1.Date, t1.ProductID, t1.StatusCD,
CASE WHEN t2.OpenCount = 0 THEN 0 ELSE 1 END AS Label
FROM yourTable t1
INNER JOIN
(
SELECT ProductID, SUM(StatusCD <> 'O') AS OpenCount
FROM yourTable
GROUP BY ProductID
) t2
ON t2.ProductID = t1.ProductID
ORDER BY
t1.Date;
Demo
I have an asset_quantities table as below
id | asset_type | quantity | site_id | asset_ids_json
1 'Container' 3 1 [{"id":1,"make":"am1","model":"amo1"},{"id":2,"make":"am1","model":"amo2"},{"id":3,"make":"am3","model":"amo3"}]
2 'Cage' 3 1 [{"id":4,"make":"bm1","model":"bmo1"},{"id":5,"make":"bm2","model":"bmo2"},{"id":6,"make":"bm2","model":"cmo3"}]
3 'Crate' 3 1 [{"id":7,"make":"cm1","model":"cmo1"},{"id":8,"make":"cm1","model":"cmo1"},{"id":9,"make":"cm1","model":"cmo2"}]
I want to write a SQL query in Postgres that will give me the quantity count of each asset type for a given make or model.
E.g. If I wanted to fetch the quantity for each asset type where make='am1',
site_id | Container_qty | Cage_qty | Crate_qty
1 2 0 0
E.g. If I wanted to fetch the quantity for each asset type where make='cm1', the result set would look like
site_id | Container_qty | Cage_qty | Crate_qty
1 0 0 3
I have written the query below to pivot the values from the 'asset_type' rows into columns but can't figure out how to filter and aggregate the counts based on the attributes inside the field 'asset_ids_json'. It is safe to assume that the length of the json array inside asset_ids_json will always be the same as the value in the 'quantity' column.
select
aq.site_id,
sum(case when aq.asset_type = 'Container' then aq.quantity end) container_qty,
sum(case when aq.asset_type = 'Cage' then aq.quantity end) cage_qty ,
sum(case when aq.asset_type = 'Crate' then aq.quantity end) crate_qty,
from asset_quantities aq
group by aq.site_id;
The crux of my question is how can I filter & aggregate results based on the attributes inside the json column 'asset_ids_json'. I'm using Postgres 9.4.
step-by-step demo:db<>fiddle
SELECT
site_id,
SUM(case when asset_type = 'Container' then quantity end) container_qty,
SUM(case when asset_type = 'Cage' then quantity end) cage_qty ,
SUM(case when asset_type = 'Crate' then quantity end) crate_qty
FROM (
SELECT DISTINCT ON (id)
site_id,
asset_type,
quantity
FROM asset_quantities aq,
json_array_elements(asset_ids_json)
WHERE value ->> 'make' = 'cm1'
) s
GROUP BY site_id
To get a WHERE clause over the content of a JSON array you have to expand the array. json_array_elements() creates one row for each element. With that it is possible to ask for a certain value.
Because of this expansion the current rows are multiplied (three times here because there are three elements in the array). Because you are only interested in the original site_id, asset_type and quantity data which were simply copied into the new records, you can eliminate them with a DISTINCT. DISTINCT ON checks for distinct values of each id. So if two JSON array would contain the same key/value both will be saved.
Query:
SELECT (CASE
WHEN a.account_number = '123456' THEN 'Savings'
WHEN a.account_number = '123321' THEN 'Credit'
END) AS "Account"
FROM accounts a
GROUP BY (CASE
WHEN a.account_number = '123456' THEN 'Savings'
WHEN a.account_number = '123321' THEN 'Credit'
END);
Output:
+---------+
| Account |
+---------+
| Savings |
| Credit |
+---------+
Desired Output:
+---------+
| Account |
+---------+
| Savings |
| Credit |
| Total |
+---------+
Given this query, how can I add a new row with the totals on the bottom?
Later I'm going to be doing more transactions...
Presumably Savings == Debit?
It might well be easier to do this in the programming language rather than the query, but could you try something like:
SELECT IFNULL((CASE
WHEN a.account_number = '123456' THEN 'Savings'
WHEN a.account_number = '123321' THEN 'Credit'
END), "Total") AS "Account"
FROM accounts a
GROUP BY a.account_number WITH ROLLUP;
I've not tried this yet so not 100% certain it will work, or how to make it named "Total" (it will be under NULL).
See WITH ROLLUP.
Looking at your diagram is it a total row that you're after? I'm a little confused.
However, if it is a total row adding all credits and debits together then i would look into the UNION statement.
basic example:
SELECT a.AccountCreditsDebits From Table
UNION
SELECT SUM(a.AccountCreditsDebits) From Table
I have a query that will display a list of products in order of their import/export property.
select i.`itemid`, p.`prodid`,
(
(case when p.`desc` like "Import/Export"
then 100 else 0 end) +
(case when p.`desc` like "Export"
then 70 else 0 end) +
(case when p.`desc` like "Import"
then 50 else 0 end)
) as priority
from item i , product p
where (
p.`name` LIKE "cleaning agent"
and p.`prodid` = i.`itemid`
)
The query does fine in adding a "priority" value to each product but what I would like to ask is how do I group them by ID and total the priority based on the ID? I can group similar prodid rows with the Order by keyword, but then it just gives me a single value for the priority field.
What I want to achieve is to group all similar product id's and get a total of their priority value. I've used sum() in select statements before, but I'm at a loss at trying to figure out how to get the total of all priority fields because it is a query-generated column.
+--------+----------+
| prodid | priority |
+--------+----------+
| 225 | 50 |
| 225 | 20 |
+--------+----------+
should be
+--------+----------+
| prodid | priority |
+--------+----------+
| 225 | 70 |
+--------+----------+
Here is a sqlfiddle: http://sqlfiddle.com/#!2/cec136/5
You can do this by turning your query into an aggregation using group by:
select p.`prodid`,
sum(case when p.`desc` like 'Import/Export' then 100
when p.`desc` like 'Export' then 70
when p.`desc` like 'Import' then 50
else 20
end) as priority
from item i join
product p
on p.`prodid` = i.`prodid`
where p.`type` LIKE 'cleaning agent'
group by p.prodid;
Along the way, I fixed a few things:
The join is now explicit in the from clause, rather than implicit in the where clause.
Because i.prodid = p.prodid, there is no need to include both in the select.
I changed the case statement to cascade. Only one of the conditions can match, so there is no reason to add things together.
I changed all the string constants to use single quotes rather than double quotes.
Once again i need yours help ;). I have a lot data and mysql request are slower and slower so the need request that i need i want group in one comand.
My example DB structure:
|product|opinion (pos/neg)|reason|
__________________________________
|milk | pos | good |
|milk | pos |fresh |
|chocolate| neg | old |
|milk | neg | from cow|
So i need information about all diffrent product (GROUP BY) count of it, and count of pos opinion for each product. I want output like that:
|product|count|pos count|
_________________________
|milk | 3 | 2 |
|chocolate| 1 | 0 |
I hope that my explain was good enought ;)
Or go to work: I write two commands
SELECT COUNT(*) as pos count FROM table WHERE product = "milk" AND opinion = "pos" GROUP BY `opinion`
And Second one
SELECT product, COUNT(*) FROM table GROUP BY `product`
I don't know how to join this two request, maybe that is impossible? In my real use code i have additional category collumn and i use WHERE in second command too
select product,
count(*) as total_count
sum(
case when opinion='pos' then 1
else 0 end
) as pos_count
from the_table
group by product;
SELECT product,
COUNT(*) TotalCount,
SUM(opinion = 'pos') POSCount
FROM tableName
GROUP BY product
SUM(opinion = 'pos') is MySQL specific syntax that counts the total of result based from the result of boolean arithmethic. If you want it to be more RDBMS friends, use CASE
SUM(CASE WHEN opinion = 'pos' THEN 1 ELSE 0 END)
SQLFiddle Demo