Trouble with Date fields & Combining data from 2 tables - mysql

I'm having a MySQL Database, created using the following code (sure there are other tables too, but they're not relevant as per this specific question) :
DROP TABLE IF EXISTS `Jeweller`.`Product_sales`;
CREATE TABLE `Jeweller`.`Product_sales` (
`sale_id` int(11) unsigned NOT NULL,
`product_id` int(11) NOT NULL,
`quantity` int(11),
`value` float,
FOREIGN KEY (`sale_id`) REFERENCES `Jeweller`.`Sales`(`id`),
FOREIGN KEY (`product_id`) REFERENCES `Jeweller`.`Products`(`id`),
CHECK (`quantity`>0),
CHECK (`value`>0)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `Jeweller`.`Products`;
CREATE TABLE `Jeweller`.`Products` (
`id` int(11) unsigned NOT NULL,
`product_category_id` int(11) NOT NULL,
`seller_id` int(11) NOT NULL,
`name` varchar(100) NOT NULL,
`description` text,
PRIMARY KEY (`id`),
FOREIGN KEY (`product_category_id`) REFERENCES `Jeweller`.`Product_categories`(`id`),
FOREIGN KEY (`seller_id`) REFERENCES `Jeweller`.`Sellers`(`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `Jeweller`.`Sales`;
CREATE TABLE `Jeweller`.`Sales` (
`id` int(11) unsigned NOT NULL,
`date` date DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
How would you go about finding :
Total earnings (quantity*value) per month (in 2013 - or any specific year for that matter)
I started by trying to get the month out of a DATE field (using DATEPART) but I'm already into trouble...
So, any ideas?
P.S.
I'm not a guru with SQL
The above is just an example, and not the exact code

SELECT MONTH(s.date) month, SUM(p.quantity * p.value)
FROM Jeweller.Sales s
JOIN Jeweller.Product_sales p ON p.sale_id = s.id
WHERE s.date >= '2013-01-01' AND s.date < '2014-01-01'
GROUP BY month
Note that if the date range spans multiple years, you will need to group on both year and month.

Related

How to get two sums from two tables in one output?

I have two tables:
Receive_Amount_Details for crediting amount from the construction site owner, and
SitewiseEmployee for debiting the amount to pay the laborer.
For every single Date, which is present in both tables, I want to:
sum all of the Amount_Received from Receive_Amount_Details as Total_Receive_Amount_from_siteowner, and
sum all of the Amount from SitewiseEmployee as Total_Amount_Payed_to_Labour
column on output table.
Both tables have the Date column, but I want a single Date column in the output.
If any single day amount is not received and labour was paid, it should be in the output table, and also if any single day amount was received but not paid for labour, then also it needs to be present in the output table.
CREATE TABLE `Receive_Amount_Details` (
`Id` int(10) NOT NULL AUTO_INCREMENT,
`SiteId` int(5) NOT NULL,
`Amount_Received` int(10) NOT NULL,
`Date` date NOT NULL,
PRIMARY KEY (`Id`),
KEY `SiteId` (`SiteId`),
CONSTRAINT `Receive_Amount_Details_ibfk_1` FOREIGN KEY (`SiteId`)
REFERENCES `SiteList` (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
and
CREATE TABLE `SitewiseEmployee` (
`Id` int(10) NOT NULL AUTO_INCREMENT,
`SiteId` int(5) NOT NULL,
`EmployeeId` int(10) NOT NULL,
`Amount` varchar(10) DEFAULT NULL,
`Date` date NOT NULL,
PRIMARY KEY (`Id`),
KEY `SiteId` (`SiteId`),
KEY `EmployeeId` (`EmployeeId`),
CONSTRAINT `SitewiseEmployee_ibfk_1` FOREIGN KEY (`SiteId`)
REFERENCES `SiteList` (`Id`),
CONSTRAINT `SitewiseEmployee_ibfk_2` FOREIGN KEY (`EmployeeId`)
REFERENCES `Employee` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1
Assuming that you may have dates in the one table that are not present in the other, and vice versa, you would need to perform a full outer join. MySql does not have such a join type, but you can achieve it with a union:
select `Date`,
sum(Amount_Received) as Sum_Amount_Received,
sum(Amount) as Sum_Amount
from (
select `Date`, Amount_Received, 0 as Amount
from Receive_Amount_Details
union
select `Date`, 0, Amount
from SitewiseEmployee
) as dates
group by `Date`;

MySQL move rows from one table to another by condition

We have a MySQL database of users, their sold and purchased products. We need to move inactive users who have not logged-in from last 3 years and never purchased or sold anything on our website to another table. Each table has millions of entries.
This is a table for sold items. Around 40m entries;
CREATE TABLE `sold` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`uid` int(10) unsigned NOT NULL,
`item` bigint(20) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
This is a table for purchased items. Around 6m entries;
CREATE TABLE `purchased` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`uid` int(10) unsigned NOT NULL,
`item` bigint(20) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
This is a user table, around 17m entries;
CREATE TABLE `users` (
`uid` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT '',
`email` varchar(64) DEFAULT NULL,
`lastlogin` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`uid`),
UNIQUE KEY `email_index` (`email`),
KEY `lastlogin` (`lastlogin`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
This is the table where we need to move inactive users to
CREATE TABLE `inactiveusers` (
`uid` int(10) unsigned NOT NULL,
`name` varchar(32) DEFAULT '',
`email` varchar(64) DEFAULT NULL,
`lastlogin` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`uid`),
UNIQUE KEY `email_index` (`email`),
KEY `lastlogin` (`lastlogin`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Any suggestions on how to achieve this with minimum downtime?
#Yumoji, I guess your probelm is How to calculate the last three 3 years of any activity. Right?
So, if that is the case then you should add a "created_at" column in your DB tables.
Now, after you add a created_at column in your tables you can easily calculate last three years activity.
Here it is-
SELECT uid FROM sold AND purchased WHERE sold.created_at>DATE_SUB(NOW(), INTERVAL 3 YEAR) OR purchased.created_at>DATE_SUB(NOW(), INTERVAL 3 YEAR);
from this query you get the user ids, Then -
INSERT INTO inactiveusers select * from users where uid = 'the user ids u get';
DELETE FROM users where uid = 'the user ids u get';
I hope it solves problem. Cheers.

MySQL Improve performance execution time

I am trying to improve performance of following query which took 93.2 sec to execute query below:
SELECT year(date), month(date), `country_name_name`,
CEIL(count(res.`user_xmpp_login`) /DAY(LAST_DAY(date))) as avgUser,
CEIL(count(res.user)/DAY(LAST_DAY(date))) as avgPurchase
FROM
( SELECT DATE(`user_registration_timestamp`) as date,
user_country,
NULL as user, `user_xmpp_login`
FROM users
WHERE `user_registration_timestamp` >= "2015-01-01 00:00:00"
AND `user_registration_timestamp` < "2016-01-01 00:00:00"
UNION ALL
SELECT DATE(`ts`) as date, user_country, user, NULL as `user_xmpp_login`
FROM purchase_log p
INNER JOIN users u ON u.`user_xmpp_login` = p.`user`
WHERE `ts` >= "2015-01-01 00:00:00"
AND `ts` < "2016-01-01 00:00:00"
AND result in ('ok', 'cancelled', 'pending')
) AS res
INNER JOIN countries c ON c.`country_id` = res.`user_country`
INNER JOIN country_names cn
ON (cn.`country_name_country` = c.`country_id`
AND cn.`country_name_language` = 'en')
GROUP BY 1,2,3
ORDER BY 4 DESC,5 DESC, 3 ASC;
Explain command shows:
And structure of each table is:
purchase table:
CREATE TABLE `purchase` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`result` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `iuser` (`user`),
) ENGINE=InnoDB AUTO_INCREMENT=12710221 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
users table:
CREATE TABLE `users` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`user_country` int(11) DEFAULT NULL,
`user_xmpp_login` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`user_registration_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`user_id`),
UNIQUE KEY `user_xmpp_login_UNIQUE` (`user_xmpp_login`),
KEY `user_country_FK` (`user_country`),
KEY `user_registration_timestamp` (`user_registration_timestamp`),
CONSTRAINT `users_country_FK` FOREIGN KEY (`user_country`)
REFERENCES `countries` (`country_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=33504745 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
countries table
CREATE TABLE `countries` (
`country_id` int(11) NOT NULL AUTO_INCREMENT,
`country_code` varchar(2) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`country_id`),
) ENGINE=InnoDB AUTO_INCREMENT=508 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
country names
CREATE TABLE `country_names` (
`country_name_id` int(11) NOT NULL AUTO_INCREMENT,
`country_name_country` int(11) NOT NULL,
`country_name_language` char(2) COLLATE utf8_unicode_ci NOT NULL,
`country_name_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`country_name_id`),
UNIQUE KEY `country_name_country_language_UNIQUE`
(`country_name_country`,`country_name_language`),
KEY `country_name_language` (`country_name_language`),
CONSTRAINT `country_name_country` FOREIGN KEY (`country_name_country`)
REFERENCES `countries` (`country_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=45793 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Is there any recommendations?
If you time each subquery, I think you will find users is the slowest component.
The purchase_log subquery can probably be improved with this "covering" INDEX(result, ts, user).
Combine the two "country" tables!. Use CHAR(2) CHARACTER SET ascii for the PRIMARY KEY and the JOINs to other tables. It is only 2 bytes, unlike INT, which is 4 bytes and VARCHAR..., which is 3 bytes (in this case).
You mention ts, but I don't see where it is coming from. If it is in purchase_log, then that table needs INDEX(user, ts).
What percentage of the users involved 2015? If it is more than about 20%, the INDEX(user_registration_timestamp) won't help.
Consider: Get rid of PRIMARY KEY (country_name_id), and promote the UNIQUE key to PRIMARY.
The biggest problem seems to be in your users table. Remember, mysql can only use one index per table for most situations. On your users table, the user_xmpp_login_UNIQUE column has been used to join it to the purchase_log table. There fore, the user_registration_timestamp index is not being used on the comparison involving the timestamp column.
One suggestion is to create a composite index on the user_xmpp_login and user_registration_timestamp columns.

sum function with join between two date ranges

hi friends i have two tables with name order and product order
CREATE TABLE `order` (
`order_num` int(11) NOT NULL AUTO_INCREMENT,
`date` date DEFAULT NULL,
`time` time DEFAULT NULL,
`cutomer_name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`order_num`)
) ENGINE=InnoDB AUTO_INCREMENT=235 DEFAULT CHARSET=latin1
And
CREATE TABLE `product_order` (
`order_num` int(11) NOT NULL,
`idproduct` int(11) NOT NULL,
`quantity` tinyint(4) DEFAULT NULL,
`sub_price` int(11) DEFAULT NULL,
`price` int(11) DEFAULT NULL,
`actual_price` int(11) DEFAULT NULL,
PRIMARY KEY (`order_num`,`idproduct`),
KEY `fk_product_order_order1_idx` (`order_num`),
KEY `fk_product_order_product1_idx` (`idproduct`),
CONSTRAINT `fk_product_order_order1` FOREIGN KEY (`order_num`) REFERENCES `order` (`order_num`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_product_order_product1` FOREIGN KEY (`idproduct`) REFERENCES `product` (`idproduct`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
i want to join both table and want to retrieve the sum of product_order.actual_price,product_order.sub_price and product_order.quantity
between specific dates
my query return zero rows and i don't know how to solve this problem
here is my query
Select `order`.date,
Sum(product_order.actual_price) as actual_price_sum,
Sum(product_order.sub_price) as sub_price_sum,
Sum(product_order.quantity) as sold_quantity,
(Sum(product_order.sub_price)-Sum(product_order.actual_price)) as profit
From `order`
Inner Join product_order On `order`.order_num = product_order.order_num
WHERE CAST(`date` AS date) BETWEEN '2014-11-11' and '2015-11-11'
Group By `order`.date

Create SQL query that gets data from several tables WHEN ID is in one table

I am trying to create a news feed and from previous questions have found the simplest way to execute it but do not know what kind of query I should be making in SQL.
I have a table titled artists that has the members ID, artist name and url to their page, all need to be retrieved. If an ID is not in this table it should not be included in the output of the statement.
I have several tables with similar structures such as tracks, status and news. There are more but we'll stick to those three for this question and I can add more late on. In these tables is the ID among other things and I shall provide these later.
The third table is one titled events. Data is inserted in here via a trigger AFTER data is added to any of the other tables I have referenced. This table contains the ID, the action (so something like 'has uploaded a track.') and the timestamp.
ARTIST TABLE;
CREATE TABLE `artists` (
`ID` int(11) NOT NULL,
`name` varchar(100) COLLATE latin1_general_ci NOT NULL,
`url` varchar(100) COLLATE latin1_general_ci NOT NULL,
`DP` varchar(200) COLLATE latin1_general_ci NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY `url` (`url`),
UNIQUE KEY `DP` (`DP`),
KEY `ID` (`ID`),
CONSTRAINT `artists_ibfk_1` FOREIGN KEY (`ID`) REFERENCES `members` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci
TRACKS
CREATE TABLE `tracks` (
`ID` int(11) NOT NULL,
`url` varchar(200) COLLATE latin1_general_ci NOT NULL,
`name` varchar(100) COLLATE latin1_general_ci NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
`track_ID` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`track_ID`),
UNIQUE KEY `url` (`url`),
UNIQUE KEY `track_ID` (`track_ID`),
KEY `ID` (`ID`),
CONSTRAINT `tracks_ibfk_1` FOREIGN KEY (`ID`) REFERENCES `members` (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=111112 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci
NEWS
CREATE TABLE `news` (
`ID` int(11) NOT NULL,
`article` text COLLATE latin1_general_ci NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
KEY `ID` (`ID`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci
STATUS
CREATE TABLE `status` (
`ID` int(11) NOT NULL,
`status` varchar(300) COLLATE latin1_general_ci NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
KEY `ID` (`ID`),
CONSTRAINT `status_ibfk_1` FOREIGN KEY (`ID`) REFERENCES `artists` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci
EVENTS
CREATE TABLE `events` (
`ID` int(11) NOT NULL,
`action` varchar(100) COLLATE latin1_general_ci NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
KEY `ID` (`ID`),
CONSTRAINT `events_ibfk_1` FOREIGN KEY (`ID`) REFERENCES `members` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci COMMENT='This table shows what the artist has done and is used feed'
The query I have tried and it failed. This one only references the tracks and events tables. I am not sure how to add the other tables to the query.
SELECT
T.url,
T.ID,
E.action,
E.ID,
E.timestamp
FROM tracks T
JOIN events E ON T.ID = E.ID
WHERE T.ID = E.ID AND E.action = 'has uploaded a track.'
ORDER BY E.timestamp DESC
To answer extremely valid comment, the query is in an AJAX requested page for a news feed. The output will be something along the lines of;
An artist has uploaded a track.
Track goes here
Formatted timestamp here
Another artist has some news.
News goes here
Formatted timestamp here
Hopefully that will give you an idea.
All data by the end of it should be ordered by timestamp. Thanks in advance.
If you have a single table with all events in it, and the message that you want to show, I'm confused why you would have to join in any of the other tables---maybe other than your member table, which would give you the user's name. Since you want each event in its own record, there's no need for any kind of aggregation. (Although, if you did want to display a comma-separated list of actions for a user, have a look at GROUP_CONCAT.)
For example a query like:
SELECT
CASE
WHEN events.table_name = "tracks" THEN "has uploaded a track."
WHEN events.table_name = "news" THEN "has some news."
END AS description
, events.action
, DATE_FORMAT(events.when_it_happened, "%a %l%p") AS when_it_happened
, members.username
, members.ID
FROM events
INNER JOIN members ON (events.ID = members.ID)
WHERE events.when_it_happened >= NOW() - INTERVAL 5 DAY
ORDER BY events.when_it_happened DESC
Would give you all events that all users performed within the last 5 days; the most recent first.