SUM in just one table - Mysql - mysql

I have the following situation. I'm setting up a stock control system and through it, I control the in and out of products.
In a table I have the products (code, description).
In the other table, record the movement of the products (code, product_code, type [in, out], quantity, date).
When I try to make the calculation of what comes in, the less that comes out, it does not work.
MY QUERY:
SELECT SUM(s1.quantity) - SUM(s2.quantity)
FROM `stock_movement` AS s1
JOIN `stock_movement` AS s2
ON s1.code_product = s2.code_product
WHERE s1.type = 'IN'
AND s2.type = 'OUT';
 
RESULTS: 80
The correct result here should be (40 + 20) - (10 + 10) = 40, but this query is considering that all inputs are of type IN and OUT, so the result 80.
Anyone have any idea what I'm doing wrong? The table data below are as follows:
TABLE STOCK_MOVEMENT:
| CODE | CODE_PRODUCT | TYPE | QUANTITY | DATA |
| 1 | 30 | IN | 20 | 2018-01-20 |
| 2 | 30 | IN | 40 | 2018-02-03 |
| 3 | 30 | OUT | 10 | 2018-01-20 |
| 4 | 30 | OUT | 10 | 2018-02-03 |
 
TABLE STOCK:
| CODE | DESCRIPTION |
| 30 | TEST_PRODUCT |

You don't need to self join here, just use conditional aggregation:
SELECT
CODE_PRODUCT,
SUM(CASE WHEN TYPE = 'IN' THEN QUANTITY ELSE 0 END) -
SUM(CASE WHEN TYPE = 'OUT' THEN QUANTITY ELSE 0 END) AS diff
FROM stock_movement
GROUP BY CODE_PRODUCT;
Note that I am aggregating by product. For your particular sample data set, there is only one product, so we could remove GROUP BY and get the same result. But in practice you might want a solution which handles multiple products.

Related

SQL Query with all data from lest column and fill blank with previous row value

After searching a lot on this forum and the web, i have an issue that i cannot solve without your help.
The requirement look simple but not the code :-(
Basically i need to make a report on cumulative sales by product by week.
I have a table with the calendar (including all the weeks) and a view which gives me all the cumulative values by product and sorted by week. What i need the query to do is to give me all the weeks for each products and then add in a column the cumulative values from the view. if this value does not exist, then it should give me the last know record.
Can you help?
Thanks,
The principal is establish all the weeks that a product could have had sales , sum grouping by week, add the missing weeks and use the sum over window function to get a cumulative sum
DROP TABLE IF EXISTS T;
CREATE TABLE T
(PROD INT, DT DATE, AMOUNT INT);
INSERT INTO T VALUES
(1,'2022-01-01', 10),(1,'2022-01-01', 10),(1,'2022-01-20', 10),
(2,'2022-01-10', 10);
WITH CTE AS
(SELECT MIN(YEARWEEK(DT)) MINYW, MAX(YEARWEEK(DT)) MAXYW FROM T),
CTE1 AS
(SELECT DISTINCT YEARWEEK(DTE) YW ,PROD
FROM DATES
JOIN CTE ON YEARWEEK(DTE) BETWEEN MINYW AND MAXYW
CROSS JOIN (SELECT DISTINCT PROD FROM T) C
)
SELECT CTE1.YW,CTE1.PROD
,SUMAMT,
SUM(SUMAMT) OVER(PARTITION BY CTE1.PROD ORDER BY CTE1.YW) CUMSUM
FROM CTE1
LEFT JOIN
(SELECT YEARWEEK(DT) YW,PROD ,SUM(AMOUNT) SUMAMT
FROM T
GROUP BY YEARWEEK(DT),PROD
) S ON S.PROD = CTE1.PROD AND S.YW = CTE1.YW
ORDER BY CTE1.PROD,CTE1.YW
;
+--------+------+--------+--------+
| YW | PROD | SUMAMT | CUMSUM |
+--------+------+--------+--------+
| 202152 | 1 | 20 | 20 |
| 202201 | 1 | NULL | 20 |
| 202202 | 1 | NULL | 20 |
| 202203 | 1 | 10 | 30 |
| 202152 | 2 | NULL | NULL |
| 202201 | 2 | NULL | NULL |
| 202202 | 2 | 10 | 10 |
| 202203 | 2 | NULL | 10 |
+--------+------+--------+--------+
8 rows in set (0.021 sec)
Your calendar date may be slightly different to mine but you should get the general idea.

SQL query SUM() AND GROUP BY

I have a MySQL table like this:
acco_id | room_id | arrival | amount | persons | available
1 | 1 | 2015-19-12 | 3 | 4 | 1
1 | 2 | 2015-19-12 | 1 | 10 | 1
1 | 1 | 2015-26-12 | 4 | 4 | 1
1 | 2 | 2015-26-12 | 2 | 10 | 1
2 | 3 | 2015-19-12 | 2 | 6 | 0
2 | 4 | 2015-19-12 | 1 | 4 | 1
What im trying to achieve is a single query with a result like:
acco_id | max_persons_available
1 | 22
2 | 4
I tried using a GROUP BY accommodation_id using a query like:
SELECT
accommodation_id,
SUM(amount * persons) as max_persons_available
FROM
availabilities
WHERE
available = 1
GROUP BY
accommodation_id
Only now the result of acco_id uses all arrival dates. When I add arrival to the query no more unique acco_id's.
Does anyone know a good Single SQL which can use the table indexes?
If I'm understanding the question correct (the last part is a bit confusing). You want to have the accomodation id and numbers as you have now but limited to specific arrival dates.
If so the following statement should do exactly that as it is not necessary to put arrival into the select if you "just" use it in the where statement. As else you would need to put it into the group by and thus have non unique accomodation id's.
SELECT
accommodation_id,
SUM(amount * persons) as max_persons_available
FROM
availabilities
WHERE
available = 1 and arrival >= '2015-12-19' and arrival < '2015-10-26'
GROUP BY
accommodation_id
I guess (reading your question) what you are looking for is this but im not sure as your question is a bit unclear:
SELECT
accommodation_id,
arrival,
SUM(amount * persons) as max_persons_available
FROM
availabilities
WHERE
available = 1
GROUP BY
accommodation_id, arrival

How to do one big select from table in MySQL?

I need to show the data from DB into a table of report file.
my_table looks like:
+----+-------+------+------+-------------------+-----------+-------+----+-------------------+
| id |entryID|userID|active| dateCreated |affiliateId|premium|free| endDate |
| 1 | 69856 | 1 | N |2014-03-22 13:54:49| 1 | N | N |2014-03-22 13:54:49|
| 2 | 63254 | 2 | Y |2014-03-21 13:35:15| 2 | Y | N | |
| 3 | 56324 | 3 | N |2014-03-21 11:11:22| 2 | Y | N |2014-02-22 16:44:46|
| 4 | 41256 | 4 | Y |2014-03-21 08:10:46| 1 | N | Y | |
| .. | ... | ... | ... | ... | ... | ... | .. | ... |
+----+-------+------+------+-------------------+-----------+-------+----+-------------------+
I need to create the table with data from my_table
| Date | № of Entries (in that date) | Total № of Entries | Premium | Free | Afiiliate |
The final table in file should looks like:
Report 17-07-2013:
+----------+--------------+-------+---------+------+-----------+
| Date | № of Entries | Total | Premium | Free | Afilliate |
|2013-07-17| 2 | 99845 | 2 | 0 | 0 |
|2013-07-18| 1 | 99843 | 0 | 1 | 0 |
|2013-07-22| 1 | 99842 | 1 | 0 | 1 |
|2013-07-23| 3 | 99841 | 2 | 1 | 2 |
|2013-07-24| 298 | 99838 | 32 | 273 | 25 |
|2013-07-25| 5526 | 99540 | 474 | 5058 | 126 |
|2013-07-26| 1686 | 94014 | 157 | 1532 | 56 |
|2013-07-27| 1673 | 92328 | 156 | 1517 | 97 |
|2013-07-28| 1461 | 90655 | 155 | 1310 | 83 |
| ... | ... | ... | ... | ... | ... |
+----------+--------------+-------+---------+------+-----------+
Should I for each column do a SELECT or I should do only 1 select?
If it possible to do 1 select how to do it?
It should be by analogy with this report:
report
Some fields differ (like 'Number of Entries in that date').
Total number of Entries means: all entries from beginning to the that specific date.
Number of Entries in that date means: all entries in that date.
In a final table the date from column Date will not repeat, that's why Column 'Number of Entries (in that date)' will calculate all entries for that date.
Your result is not so clear for the total is a count or sum and affiliate is sum or count also
but assuming total will be count and affiliate will be sum
here a query you might use to give you a result ( using ms-sql )
select DateCreated,count(EntryId) as Total,
sum(case when Premium='Y' then 1 else 0 end) as Premium,
sum(case when Premium='N' then 1 else 0 end) as Free,
sum(AffiliateId) as Affiliate
from sample
group by DateCreated
here a working demo
if I didn't understood you correctly, kindly advise
hope it will help you
SQLFiddle Demo: http://sqlfiddle.com/#!9/20cc0/5
The added column entryID does not matter for us.
I don't really understand what you want for Total, or the criteria for affiliateID. This query should get you started.
SELECT
DATE(dateCreated) as "Date",
count(dateCreated) as "No of Entries",
99845 as Total,
sum( case when premium='Y' then 1 else 0 end ) as Premium,
sum( case when premium='N' then 1 else 0 end ) as Free,
sum( case when affiliateID IS NOT NULL then 1 else 0 end) as Affiliate
FROM MyTable
GROUP BY DATE(dateCreated)
ORDER BY Date ASC
The final table in file should looks like:
... This new table can be in a file or in the web page. But it is not a new table in DB. –
It sounds like you may be new to this area so I just wanted to inform you that spitting out a report into a file for a website is highly unusual and typically only done when your data is completely separate from the website. Putting data from a database onto a website (like the query we made here) is very common and it's very likely you don't need to mess with any files.
select date(DateCreated),count(entryId) as Total,
sum(case when Premium='Y' then 1 else 0 end) as Premium,
sum(case when Premium='N' then 1 else 0 end) as Free,
sum( case when affiliateID IS NOT NULL then 1 else 0 end) as Affiliate
INTO OUTFILE '/tmp/myfile.csv'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
from my_table
group by date(DateCreated) order by date(DateCreated);

how to group mysql based on items and today date

I have a database that store transaction logs, I would like to count all the logs for that day and group them based on prod_id
MySQL table structure:
Table name = products
+------+---------+------------+--------+
| ID | PROD_ID | DATE | PERSON |
+------+---------+------------+--------+
| 1 | 2 | 1400137633 | 1 |
| 2 | 2 | 1400137666 | 1 |
| 3 | 3 | 1400137125 | 2 |
| 4 | 4 | 1400137563 | 1 |
| 5 | 2 | 1400137425 | 2 |
| 6 | 3 | 1400137336 | 1 |
+------+---------+------------+--------+
MYSQL CODE:
$q = 'SELECT count(ID) as count
FROM PRODUCTS
WHERE PERSON ='.$db->qstr($person).'
AND DATE(FROM_UNIXTIME(DATE)) = DATE(NOW())';
so what I get is the number of items for the given date. Since the date is the same as all other entries. however I would like to group the items by prod_id, I tried GROUP BY PROD_ID but that did not give me what I want. I would like it to group if the PROD_ID is multiple and the date is the same display as one entry while still count the others
so here I should get an output ($Person = 1).... 2+2+2=1 +3 +4 so total should be 3
any suggestions?
Use DISTINCT with COUNT on PROD_ID.
Example:
SELECT count( distinct PROD_ID ) as count
FROM PRODUCTS
WHERE PERSON = 1 -- <---- change this with relevant variable
AND DATE( FROM_UNIXTIME (DATE ) ) = curdate();
And I suggest you to use Prepared Statement to bind values.

How to improve the fields name

I am trying to improve the fields name to be more obvious what it means.. is the fields name ok? or what can be changed to make it more obvious?
mysql> select * from order_items;
+----+----------+--------+----------+------------+-------+
| id | order_id | name | quantity | item_price | total |
+----+----------+--------+----------+------------+-------+
| 1 | 5 | Item 1 | 2 | 3.00 | 6.00 |
| 2 | 5 | Item 2 | 1 | 2.00 | 2.00 |
+----+----------+--------+----------+------------+-------+
mysql> select * from orders;
+----+---------+---------+-----------------+---------------+----------------+----------+---------------+------------+----------------+------------+----------------------+--------------------+--------------------+----------------------+
| id | shop_id | user_id | shipping_method | shipping_fees | payment_method | card_fee | commision_fee | item_total | customer_total | shop_total | shop_total_remaining | our_commission_net | our_commission_vat | our_commission_gross |
+----+---------+---------+-----------------+---------------+----------------+----------+---------------+------------+----------------+------------+----------------------+--------------------+--------------------+----------------------+
| 5 | 29 | 9 | delivery | 1.00 | card | 0.50 | 13 | 8.00 | 9.50 | 9.00 | 7.83 | 1.17 | 0.23 | 1.40 |
+----+---------+---------+-----------------+---------------+----------------+----------+---------------+------------+----------------+------------+----------------------+--------------------+--------------------+----------------------+
Fields name description:
item_total Total cost from order_items.order_id = 5
customer_total Total cost for customer to pay (item_total + card_fee + shipping_fee)
shop_total Total order for the shop (item_total + shipping_fees)
shop_total_remaining Total remaining to pay back to shop (shop_total - our_commission_net)
our_commission_net Commission I will make (commission_fee * shop_total / 100)
our_commission_gross Commission inc VAT (our_commission_net + our_commission_vat)
Did I add unnecessary fields?
I used PHP to calculate the cost.
I can see you've actually added many fields that where not necessary. Think of it this way: If you can explain how to calculate a field by applying any operations to other fields, then it is a calculated field.
Those fields shouldn't be part of a minimalistic design as they can be obtained by operating over other fields. Let's go for the most complex example in your tables.
If you wanted to get the total of all the items for an order in your example you would just select the item_total field. But what happens if it wasn't there? Could you still get that number? Yes. This is how:
select sum(oi.total) from order_items oi
inner join order o on (oi.order_id = o.id)
where (o.id = 5)
Now, we've got rid of one field, but can we remove the order_items.total field and still get this result? Yes. Because it is also a calculated field. This is how:
select sum(oi.quantity * oi.item_price) from order_items oi
inner join order o on (oi.order_id = o.id)
where (o.id = 5)
Applying a similar pattern you can get rid of all the fields you've mentioned. And then you'll have a minimal design.
One thing that worths mentioning is that calculating fields is more complex than just querying the value so they are more expensive in terms of CPU and HD usage. The advantage of calculated fields is that you avoid data redundancy and save a bit of space too :)