Skip to content
Advertisement

Reuse select aliases in MySQL

I currently have a query where I’m doing two subqueries to get X, Y data:

SELECT
  t.series AS week,
  ( ... ) X,
  ( ..., AND ... ) Y,
  ROUND(( ... ) * 100) / ( ..., AND ... ), 2) Z
FROM series_tmp t

Y is kind of subset of X, since I apply just an additional condition to the existing ones, if X is:

SELECT COUNT(*)
FROM t1
INNER JOIN t2
ON t2.id = t1.another_id
WHERE t2.something = 1
AND t1.date BETWEEN t.series AND t.series + INTERVAL 6 DAY

Then Y has an additional AND condition:

SELECT COUNT(*)
FROM t1
INNER JOIN t2
ON t2.id = t1.another_id
WHERE t2.something = 1
AND t1.date BETWEEN t.series AND t.series + INTERVAL 6 DAY
AND t1.some_state = 'x state'

And for the value of X I need to take those two results – X and Y and do some calculation. Since I can’t use the aliases, I have to use a subquery, right? But in that case it seems too much.

Is there a way to reuse those subqueries? It seems to be too much of the same.

series_tmp is a temporary table used to create a range of dates with a specific step:

CALL generate_series(DATE('2020-08-23' - INTERVAL 12 WEEK), '2020-08-23', 'INTERVAL 7 DAY');

SELECT * FROM series_tmp;

'2020-05-31'
'2020-06-07'
'2020-06-14'
'2020-06-21'
'2020-06-28'
'2020-07-05'
'2020-07-12'
'2020-07-19'
'2020-07-26'
'2020-08-02'
'2020-08-09'
'2020-08-16'
'2020-08-23'

The INNER JOIN used is the only one in the query which is just used to compare against primary/foreign key to further filter by the value of t2.something (t2.something = 1).

The columns I’m using from t1 are just date and some_state, the count is performed using the wildcard.

This is to reproduce the scenario:

DROP TABLE IF EXISTS t3;
CREATE TABLE t3 (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (id)
);
DROP TABLE IF EXISTS t2;
CREATE TABLE t2 (
  id int(11) NOT NULL AUTO_INCREMENT,
  t3_id int(11) NOT NULL,
  PRIMARY KEY (id)
);
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (
  id int(11) NOT NULL AUTO_INCREMENT,
  t2_id int(11) NOT NULL,
  created_at datetime NOT NULL,
  status varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (id)
);

INSERT INTO t3 (name) VALUES ('t3 #1');
INSERT INTO t2 (t3_id) VALUES (1);
INSERT INTO t1 (t2_id, status, created_at) VALUES   
('1', 'sent',      '2020-06-03 00:00:00'),
('1', 'sent',      '2020-06-08 00:00:00'),
('1', 'sent',      '2020-06-19 00:00:00'),
('1', 'responded', '2020-06-26 00:00:00'),
('1', 'responded', '2020-06-27 00:00:00'),
('1', 'sent',      '2020-06-30 00:00:00'),
('1', 'sent',      '2020-07-08 00:00:00'),
('1', 'sent',      '2020-07-09 00:00:00'),
('1', 'responded', '2020-07-14 00:00:00'),
('1', 'sent',      '2020-07-21 00:00:00'),
('1', 'sent',      '2020-07-22 00:00:00'),
('1', 'sent',      '2020-07-25 00:00:00'),
('1', 'responded', '2020-07-30 00:00:00'),
('1', 'sent',      '2020-08-02 00:00:00'),
('1', 'sent',      '2020-08-10 00:00:00'),
('1', 'responded', '2020-08-18 00:00:00'),
('1', 'responded', '2020-08-19 00:00:00');

-- expected result would be:

-- '2020-05-31','1','0','0.00'
-- '2020-06-07','1','0','0.00'
-- '2020-06-14','1','0','0.00'
-- '2020-06-21','2','2','100.00'
-- '2020-06-28','1','0','0.00'
-- '2020-07-05','2','0','0.00'
-- '2020-07-12','1','1','100.00'
-- '2020-07-19','3','0','0.00'
-- '2020-07-26','1','1','100.00'
-- '2020-08-02','1','0','0.00'
-- '2020-08-09','1','0','0.00'
-- '2020-08-16','2','2','100.00'

The SQLfiddle http://sqlfiddle.com/#!9/45f389.

I’m using MySQL 5.6 so I’m not able to use CTEs 🙁

PS: series_tmp comes from [this].

Advertisement

Answer

You can’t use CTEs in MySQL* but you can use temporary tables which is a close MySQL equivalent (not sure how you calculate Z so I didn’t put that one in):

CREATE TEMPORARY TABLE temp_t3 AS (
    SELECT t1.created_at, t1.status
    FROM t1
    INNER JOIN t2 ON t2_id = t2.id
    WHERE t2.t3_id = 1
);

SELECT
    t.series AS week,
    (SELECT COUNT(*) FROM temp_t3 WHERE created_at BETWEEN t.series AND t.series + INTERVAL 6 DAY) X,
    (SELECT COUNT(*) FROM temp_t3 WHERE created_at BETWEEN t.series AND t.series + INTERVAL 6 DAY AND status = 'responded') Y
FROM series_tmp t;

*prior to version 8 (https://dev.mysql.com/doc/refman/8.0/en/with.html)

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement