Related
I have a query involving dates that is really close to being complete, but I can't quite get the last part. The table columns are:
purchased TINYINT
purchased_date DATE
expired TINYINT
expiration_date DATE
The query I'm running is this:
SELECT
e_date,
num_interactions,
#runningTotal := #runningTotal + totals.num_interactions AS runningTotal
FROM
(SELECT
DATE(purchase_date) AS e_date,
COUNT(*) AS num_interactions
FROM domain_names AS d
WHERE purchased = 1
AND purchase_date != "0000-00-00"
GROUP BY DATE(d.purchase_date)) totals
ORDER BY e_date;
It works well, but I need one final adjustment. I need to exclude/subtract the names from the running total that were expired (based on expiration date) after the e_date.
In other words, if the running total shouldn't count a domain after the date it has expired. Thank you so much for any advice here. This is my first post here, so hopefully I've explained it clearly.
EDIT:
I've put together an MCRE:
CREATE TABLE IF NOT EXISTS `domain_names` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`purchased` tinyint(1) DEFAULT NULL,
`purchase_date` date DEFAULT NULL,
`expired` tinyint(1) DEFAULT NULL,
`expiration_date` date DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_domain_names_on_id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO `domain_names` (`id`, `name`, `purchased`, `purchase_date`, `expired`, `expiration_date`) VALUES
('1', 'example1.com', '1', '2019-01-12', NULL, '2019-08-12'),
('2', 'example2.com', '1', '2019-01-12', NULL, '2020-10-12'),
('3', 'example3.com', '1', '2019-01-12', '1', '2019-10-12'),
('4', 'example4.com', '1', '2019-10-12', NULL, '2020-10-12'),
('5', 'example2.com', '1', '2019-03-19', NULL, '2020-03-19'),
('6', 'example3.com', '1', '2019-08-19', NULL, '2020-08-10'),
('7', 'example4.com', '1', '2019-11-28', NULL, '2020-10-12');
http://sqlfiddle.com/#!9/2d9f71/1/1
Here is a different approach not relying on variables and subqueries
SELECT
DATE(d.purchase_date),
COUNT(DISTINCT d.id) AS num_interactions,
COUNT(DISTINCT c.id) AS cumulative_sum_not_expired
FROM domain_names AS d
LEFT JOIN domain_names AS c ON (DATE(c.purchase_date) <= DATE(d.purchase_date)
AND c.purchased = 1
AND c.purchase_date != 0
AND c.expiration_date >= CURDATE())
WHERE d.purchased = 1
AND d.purchase_date != 0
GROUP BY 1
ORDER BY 1;
http://sqlfiddle.com/#!9/2d9f71/36/0
You can adjust the exclusion here :
c.expiration_date >= CURDATE()
if you need exclude expired domains, then you should add where clause expiration_date > CURDATE() to couning subquery or count only not expired domains COUNT(case when expiration_date > CURDATE() then 1 end)...
SET #runningTotal := 0;
SET #runningTotalNE := 0;
SELECT
e_date,
num_interactions,
#runningTotal := #runningTotal + totals.num_interactions AS runningTotal,
#runningTotalNE := #runningTotalNE + totals.notexpired AS runningTotalNotExpired
FROM
(SELECT
purchase_date AS e_date,
COUNT(*) AS num_interactions,
COUNT(case when expiration_date > CURDATE() then 1 end) AS notexpired
FROM domain_names AS d
WHERE purchased = 1
/*AND expiration_date > CURDATE()*/
AND purchase_date != "0000-00-00"
GROUP BY d.purchase_date) totals
ORDER BY e_date
I have a source table (TableA) that contains multiple records for each day. I need to left join (on the date field) it to TableB that contains a few records per year.
The problem is that TableA should be joined to the earliest record from TableB where the date from TableA <= the date from TableB.
CREATE TABLE IF NOT EXISTS `tableA` (
`id` int(6) unsigned NOT NULL,
`date` date NOT NULL,
`content` varchar(200) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `tableA` (`id`, `date`, `content`) VALUES
('1', '2017-10-03', 'The earth is round.'),
('2', '2018-01-01', 'The earth is flat'),
('3', '2018-01-01', 'One hundred angels can dance on the head of a pin'),
('4', '2018-01-02', 'The earth is flat and rests on a bull\'s horn'),
('5', '2018-01-03', 'The earth is like a ball.');
CREATE TABLE IF NOT EXISTS `tableB` (
`date` date NOT NULL,
`content` varchar(200) NOT NULL,
PRIMARY KEY (`date`)
) DEFAULT CHARSET=utf8;
INSERT INTO `tableB` (`date`, `content`) VALUES
('2017-01-01', 'ONE'),
('2017-12-01', 'TWO'),
('2018-01-02', 'THREE'),
('2018-01-05', 'FOUR');
Based on the this SQLFiddle, I'm looking for the following result.
tableA.id | tableB.content
--------------------------
1 | TWO
2 | THREE
3 | THREE
4 | THREE
5 | FOUR
Here is one solution:
SELECT a.id, b.content
FROM TableA a
JOIN TableB b ON b.date = (
SELECT MIN(b2.date)
FROM TableB b2
WHERE b2.date >= a.date
);
I'm not sure whether this is the most efficient way, but it works.
I have a small business and I made a simple mysql database for entering Client name, Datetime, Details, Invoice, Signer, Billing total, Month to date, Year to date, Parts purchased, Parts Purchased year to date. I would like to automatically update the dollar amounts, is this possible? For example: Month to date is $1300.00 the next
entry is for $100.00 leaving a total of $1400.00.
Any help would be greatly appreciated.
You'll want to store your information in such a way that it is accurate and efficient. Then use queries to pull the information you are looking for for month to date and year to date.
Lets create a test database and a few tables. You can add columns as needed.
Test database
CREATE DATABASE stackoverflow;
USE stackoverflow;
Create the table for clients
This will store all your clients
CREATE TABLE clients (
client_id INT NOT NULL PRIMARY KEY auto_increment,
client_name VARCHAR( 70 ) NOT NULL,
client_details TEXT
) engine = myisam DEFAULT charset=latin1;
Create the table for parts
This will store all the parts that you wish to invoice client for
CREATE TABLE parts (
part_id INT NOT NULL PRIMARY KEY auto_increment,
part_name VARCHAR( 70 ) NOT NULL,
part_cost NUMERIC( 15,2 ),
description TEXT
) engine = myisam DEFAULT charset=latin1;
Create the table for invoices
This will store each invoice that is created. Notice that there is no dollar values or parts here. Doing it this way will reduce the number of times that identical information has to be stored.
Database normalization is the process of organizing the attributes and tables of a relational database to minimize data redundancy. Wikipedia
CREATE TABLE invoices (
invoice_id INT NOT NULL PRIMARY KEY auto_increment,
client_id INT NOT NULL,
invoice_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
details TEXT,
signer VARCHAR ( 70 ) NOT NULL
) engine = myisam DEFAULT charset=latin1;
Create table to connect parts to invoices
Now, lets create a table that will connect our parts table with the invoices. Here we can add information like the notes and quantity that is unique to each item for each invoice. We will use the quantity in our queries to calculate our totals, MTD, YTD, etc.
CREATE TABLE invoice_parts (
id INT NOT NULL PRIMARY KEY auto_increment,
part_id INT NOT NULL,
invoice_id INT NOT NULL,
quantity INT NOT NULL,
notes TEXT
) engine = myisam DEFAULT charset=latin1;
Lets add some test information
-- add some clients
INSERT INTO `clients` (`client_id`, `client_name`, `client_details`) VALUES (NULL, 'chad barker', 'some guy named chad'), (NULL, 'leon tester', 'some guy named leon');
-- add some parts
INSERT INTO `parts` (`part_id`, `part_name`, `part_cost`, `description`) VALUES (NULL, 'can of awesome', '1.99', 'awesome can of awesome'), (NULL, 'can of fail', '.25', 'can filled with failure'), (NULL, 'box of blocks', '24.99', 'box full of cube shaped items called blocks'), (NULL, 'bag of air', '3.99', 'reusable bag filled with useless air');
-- add some invoices
INSERT INTO `invoices` (`invoice_id`, `client_id`, `invoice_date`, `details`, `signer`) VALUES (NULL, '1', CURRENT_TIMESTAMP, 'there was an order for some stuff', ''), (NULL, '1', CURRENT_TIMESTAMP, 'more stuff needed now', ''), (NULL, '2', CURRENT_TIMESTAMP, 'need some stuff quick', ''), (NULL, '2', CURRENT_TIMESTAMP, 'don''t ship, just charge me', '');
-- add some invoice/part connections
INSERT INTO `invoice_parts` (`part_id`, `invoice_id`, `quantity`, `notes`) VALUES ('3', '1', '3', 'nothing special'), ('2', '1', '2', 'wants blue'), ('4', '2', '32', 'wants every color'), ('3', '2', '31', 'wants clear'), ('2', '3', '12', 'wants blue'), ('1', '4', '2', 'wants every color'), ('1', '1', '3', 'nothing special'), ('2', '1', '2', 'wants blue'), ('3', '2', '22', 'wants every color'), ('4', '2', '3', 'wants clear'), ('2', '3', '12', 'wants blue'), ('3', '4', '2', 'wants every color');
Query the invoice information
SELECT
c.client_name,
i.invoice_date,
i.details,
i.signer
FROM invoices i
INNER JOIN clients c
ON i.client_id = c.client_id
WHERE i.invoice_id = 1
;
Query the list of parts
SELECT
ip.invoice_id,
p.part_id,
p.part_name,
p.part_cost,
ip.quantity,
p.part_cost * ip.quantity as total
FROM invoice_parts ip
INNER JOIN parts p
ON ip.part_id = p.part_id
WHERE ip.invoice_id = 1;
Query the total cost of an invoice
SELECT
ip.invoice_id,
SUM( p.part_cost * ip.quantity ) as invoice_total
FROM invoice_parts ip
INNER JOIN parts p
ON ip.part_id = p.part_id
WHERE ip.invoice_id = 1;
Query by a selected month
SELECT
month( i.invoice_date ) as month,
count( distinct ip.invoice_id ) as invoices,
count( * ) as parts,
p.part_cost * ip.quantity as total
FROM invoices i
INNER JOIN invoice_parts ip
ON ip.invoice_id = i.invoice_id
INNER JOIN parts p
ON ip.part_id = p.part_id
WHERE i.client_id = 2
AND month( i.invoice_date ) = 5;
Query by a selected year
SELECT
year( i.invoice_date ) as year,
count( distinct ip.invoice_id ) as invoices,
count( * ) as parts,
p.part_cost * ip.quantity as total
FROM invoices i
INNER JOIN invoice_parts ip
ON ip.invoice_id = i.invoice_id
INNER JOIN parts p
ON ip.part_id = p.part_id
WHERE i.client_id = 1
AND year( i.invoice_date ) = 2014;
How can I select a row from another table based on the sum of column from the left table
SELECT Group_concat(c.cartid SEPARATOR ',') AS CartIDs,
Sum(c.grandtotal) AS Sum,
r.percentage
FROM carts c
LEFT JOIN rebates r
ON Sum(c.grandtotal) >= r.fromamountpurchased
AND Sum(c.grandtotal) <= r.toamountpurchased
WHERE c.ispaid = '1'
AND c.addedtorebates = '0'
GROUP BY c.customerid
But this doesn't work. I also tried HAVING also doesn't work.
Is it possible in one query?
Thanks!
UPDATE:
CREATE TABLE IF NOT EXISTS `carts` (
`CartID` bigint(20) NOT NULL AUTO_INCREMENT,
`CustomerID` bigint(20) NOT NULL,
`GrandTotal` decimal(10,2) NOT NULL,
`IsPaid` enum('0','1','2') NOT NULL,
`AddedToRebates` enum('0','1') NOT NULL,
PRIMARY KEY (`CartID`)
)
INSERT INTO `carts` (`CartID`, `CustomerID`, `GrandTotal`, `IsPaid`,
`AddedToRebates`, ) VALUES
(71, 28, '57450.00', '1', '0' ),
(73, 28, '57450.00', '1', '0');
CREATE TABLE IF NOT EXISTS `rebates` (
`RebateID` bigint(20) NOT NULL AUTO_INCREMENT,
`Percentage` varchar(255) NOT NULL COMMENT 'in %',
`FromAmountPurchased` decimal(10,2) NOT NULL,
`ToAmountPurchased` decimal(10,2) NOT NULL,
`CashEquivalent` decimal(10,2) NOT NULL,
PRIMARY KEY (`RebateID`)
)
INSERT INTO `rebates` (`RebateID`, `Percentage`, `FromAmountPurchased`,
`ToAmountPurchased`, `CashEquivalent`) VALUES
(1, '5', '50000.00', '69999.00', '3000.00'),
(2, '10', '70000.00', '79999.00', '5000.00'),
(3, '15', '80000.00', '89999.00', '6000.00'),
(4, '20', '90000.00', '99999.00', '7000.00'),
(5, '25', '100000.00', '150000.00', '8000.00'),
(6, '0', '0.00', '49999.00', '0.00');
Try this:
select q1.CartIDs, q1.total, r.percentage
from
(select group_concat(c.cartid) as CartIDs, sum(c.grandtotal) as total
from carts c
where c.ispaid = '1'
and c.addedtorebates = '0'
group by c.customerid ) q1
left join rebates r
on q1.total >= r.fromamountpurchased
and q1.total <= r.toamountpurchased
Here is a demo fiddle for you: http://sqlfiddle.com/#!9/d27f5/3
You cannot use aggregate functions like SUM() in the join predicate, so in this instance, a subquery is useful
You can achieve your result with a sub query. Please note that this sub query requires an additional scan of carts.
SELECT GROUP_CONCAT(c.CartID SEPARATOR ',') AS CartIDs, SUM(c.GrandTotal) as Sum, r.Percentage
FROM carts c
INNER JOIN (
SELECT SUM(GrandTotal) as grandTotal, CustomerID
FROM carts
GROUP BY CustomerID
) cSums ON cSums.CustomerID = c.CustomerID
LEFT JOIN rebates r ON cSums.grandTotal >= r.FromAmountPurchased AND cSums.grandTotal <= r.ToAmountPurchased
WHERE c.IsPaid = '1' AND c.AddedToRebates = '0' GROUP BY c.CustomerID
I have two table one is 'tb_student' and other is 'tb_fees'
create query for 'tb_student'
CREATE TABLE `tb_student` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`class` varchar(255) NOT NULL,
`created_on` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
)
create query for 'tb_fees'
CREATE TABLE `tb_fees` (
`id` int(11) NOT NULL auto_increment,
`email` varchar(255) NOT NULL,
`amount` varchar(255) NOT NULL,
`created_on` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
)
In first table i am storing the student details and in other table storing the fees details
I want to select student details from 'tb_student' and last add fee from 'tb_fees' only for those student which are in class 6
so i tried this
SELECT *
FROM tb_student s INNER JOIN
tb_fees f on
s.email =f.email
WHERE s.class = 6 GROUP BY s.email ORDER BY f.created_on DESC
This will give result only the first created how to get last created values
fees table
insert into `tb_fees`(`id`,`email`,`amount`,`created_on`) values (5,'ram#gmail.com','5000','2013-05-01 14:20:15');
insert into `tb_fees`(`id`,`email`,`amount`,`created_on`) values (6,'Sam#gmail.com','5000','2013-05-02 14:20:23');
insert into `tb_fees`(`id`,`email`,`amount`,`created_on`) values (7,'jak#gmail.com','5000','2013-05-03 14:20:30');
insert into `tb_fees`(`id`,`email`,`amount`,`created_on`) values (8,'Sam#gmail.com','5000','2013-05-29 14:20:35');
insert into `tb_fees`(`id`,`email`,`amount`,`created_on`) values (9,'ram#gmail.com','5000','2013-05-30 14:20:39');
insert into `tb_fees`(`id`,`email`,`amount`,`created_on`) values (10,'jak#gmail.com','5000','2013-05-30 14:36:13');
insert into `tb_fees`(`id`,`email`,`amount`,`created_on`) values (11,'rose#gmail.com','5000','2013-05-30 14:36:15');
insert into `tb_fees`(`id`,`email`,`amount`,`created_on`) values (12,'nim#gmail.com','5000','2013-05-30 14:36:15');
Student table values
insert into `tb_student`(`id`,`name`,`email`,`class`,`created_on`) values (5,'Ram','ram#gmail.com','6','2013-04-30 14:00:56');
insert into `tb_student`(`id`,`name`,`email`,`class`,`created_on`) values (6,'Sam','Sam#gmail.com','6','2013-03-30 14:01:30');
insert into `tb_student`(`id`,`name`,`email`,`class`,`created_on`) values (7,'Nimmy','nim#gmail.com','7','2013-04-30 13:59:59');
insert into `tb_student`(`id`,`name`,`email`,`class`,`created_on`) values (8,'jak','jak#gmail.com','6','2013-03-30 14:07:32');
insert into `tb_student`(`id`,`name`,`email`,`class`,`created_on`) values (9,'rose','rose#gmail.com','5','2013-04-30 14:07:51');
Thank you
To get the latest fees something like this:-
SELECT s.* , f.*
FROM tb_student s
INNER JOIN
(SELECT email, MAX(created_on) AS created_on
FROM tb_fees
GROUP BY email) Sub1
ON s.email = sub1.email
INNER JOIN tb_fees f
ON s.email = f.email AND Sub1.created_on = f.created_on
WHERE s.class = 6
By the way, you probably want indexes on the email fields (or better, use the tb_student id field on the tb_fees table instead of the email field and index it)
Use MAX group function
SELECT s.*, f.amount,MAX(f.created_on)
FROM tb_student s
INNER JOIN
tb_fees f
ON
s.email =f.email
WHERE s.class = 6
GROUP BY s.email