Related
There is a table objects, which stores data on real estate objects. Me need to use a query to calculate a new field that will display the date range from Monday to Sunday, which includes the date the object was created (for example, “2020-11-16 - 2020-11-22”)
create table objects(
object_id int NOT NULL PRIMARY KEY ,
city_id int not null ,
price int ,
area_total int ,
status varchar(50) ,
class varchar(50) ,
action varchar(50) ,
date_create timestamp,
FOREIGN KEY(city_id) references avg_price_square_city(city_id)
);
Data in the table:
INSERT INTO objects (object_id, city_id, price, area_total, status, class, action, date_create)
VALUES (1, 1, 4600000, 72, 'active', 'Secondary', 'Sale', '2022-05-12 21:49:34');
INSERT INTO objects (object_id, city_id, price, area_total, status, class, action, date_create)
VALUES (2, 2, 5400000, 84, 'active', 'Secondary', 'Sale', '2022-05-19 21:49:35');
The query should display two fields: the object number and a range that includes the date it was created. How can this be done ?
P.S
I wrote this query,but he swears at the "-" sign:
SET #WeekRangeStart ='2022/05/10';
SET #WeekRangeEnd = '2022/05/17';
select object_id,#range := #WeekRangeStart '-' #WeekRangeEnd
FROM objects where #range = #WeekRangeStart and date_create between #WeekRangeStart and #WeekRangeEnd
UNION
select object_id,#range from objects where #`range` = #WeekRangeEnd;
Error:[42000][1064] You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '#WeekRangeEnd FROM objects where #range = #WeekRangeStart and date_create betwee' at line 1
I want to receive in query:
object_id #range
1 2022/05/10 - 2022/05/17
The column #range must contain the date from the "date_create"
SET #WeekRangeStart = CAST('2022/05/10' as DATE);
SET #WeekRangeEnd = CAST('2022/05/17' as DATE);
SET #range = CONCAT(#WeekRangeStart,' - ',#WeekRangeEnd) ;
-- select #range;
select
object_id,
#range
FROM objects
where DATE(date_create) between #WeekRangeStart and #WeekRangeEnd
UNION
select object_id,#range from objects
;
Gives next result:
object_id
#range
1
2022-05-10 - 2022-05-17
2
2022-05-10 - 2022-05-17
This result is the output of the SQL part that is put after the UNION. Because date_create is not between your WeekRangeStart and WeekRangeEnd.
You should take some time, and read the UNION documentation.
The variable #range is calculated before the SQL statement, because the value is a constant.
see: DBFIDDLE
NOTE: You should try to use the same dateformat everywhere, and not mix date like '2022-05-19 21:49:35' and 2022/05/10. Use - OR use /, but do not mix them...
EDIT: After the calirification "Me need to use a query to calculate a new field that will display the date range from Monday to Sunday,...":
You probably wanted to do:
SET #WeekDate = CAST('2022/05/10' as DATETIME);
SELECT
ADDDATE(#WeekDate, -(DAYOFWEEK(#WeekDate)-1) +1) as Monday,
DATE_ADD(ADDDATE(#WeekDate, -(DAYOFWEEK(#WeekDate)-1) +9), INTERVAL -1 SECOND) as Sunday;
output:
Monday
Sunday
2022-05-09 00:00:00
2022-05-16 23:59:59
I've got the use case to version objects (identified by objectOwnerId and objectId group). I insert rows to ledger table with their respective hashes.
The order of the ledger table is identified by the compound PRIMARY KEY and its timestamp up to microsecond precision + additional 3 byte entropy at the end to prevent collisions (in case multiple rows gets inserted at the same microsecond).
Once data is stored I need efficient way to get the latest hash for multiple objects at once. I've came up with a query (please see end of this post) which is built from sub-selects with JOIN and GROUP BY, but it's pretty complex I think and I am looking for ways to address my problem in a simpler (if possible) way.
Is there any way for improvement?
It would've been simpler if I have PRIMARY KEY which isn't COMPOUND, in which case I could pass the max() value upwards, however that's not the case. I was also thinking if I could merge my TIMESTAMP(6) - 7 bytes with BINARY(3) - 3 bytes and store it as BINARY(10), but wasn't sure if that's easily possible.
Please find the schema, test data and SELECT queries below.
This is my table:
CREATE TABLE `ledger` (
`objectOwnerId` CHAR(10) NOT NULL,
`objectId` VARCHAR(50) NOT NULL,
`objectHash` BINARY(16) NOT NULL,
`timestamp` TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
`timestampAdditionalEntropy` BINARY(3) NOT NULL,
PRIMARY KEY (`timestamp`, `timestampAdditionalEntropy`),
UNIQUE(`objectHash`),
INDEX(`objectId`(10))
);
Let's insert some values:
INSERT INTO ledger (objectOwnerId, objectId, objectHash, timestampAdditionalEntropy) VALUES ('owneraaaaa', 'ida', unhex(substring(sha1(random_bytes(16)), 1, 32)), random_bytes(3));
INSERT INTO ledger (objectOwnerId, objectId, objectHash, timestampAdditionalEntropy) VALUES ('owneraaaaa', 'ida', unhex(substring(sha1(random_bytes(16)), 1, 32)), random_bytes(3));
INSERT INTO ledger (objectOwnerId, objectId, objectHash, timestampAdditionalEntropy) VALUES ('owneraaaab', 'idb', unhex(substring(sha1(random_bytes(16)), 1, 32)), random_bytes(3));
INSERT INTO ledger (objectOwnerId, objectId, objectHash, timestampAdditionalEntropy) VALUES ('owneraaaab', 'idb', unhex(substring(sha1(random_bytes(16)), 1, 32)), random_bytes(3));
INSERT INTO ledger (objectOwnerId, objectId, objectHash, timestampAdditionalEntropy) VALUES ('owneraaaab', 'idb', unhex(substring(sha1(random_bytes(16)), 1, 32)), random_bytes(3));
We've got this dataset:
# objectOwnerId, objectId, objectHash, timestamp, HEX(CAST(timestampAdditionalEntropy AS CHAR(6) CHARACTER SET utf8))
#'owneraaaab', 'idb', 'A8D3B63EFC6C63FD996B8D1931FBF748', '2019-05-29 11:38:12.353521', '725E3D'
#'owneraaaab', 'idb', '9B7395F9EE2F2363BA89C7FBAEDDBB54', '2019-05-29 11:38:12.352524', '8B8162'
#'owneraaaab', 'idb', '80393C5FF4492342D073B5F8B3388EC2', '2019-05-29 11:38:12.351569', 'FEAA02'
#'owneraaaaa', 'ida', '0D84F725ACAC87838C34742CA00BBEF7', '2019-05-29 11:38:12.350648', '41E425'
#'owneraaaaa', 'ida', '9A82C936A25C4648BFB75B692850841B', '2019-05-29 11:38:12.349625', '470685'
returned by this query:
select objectOwnerId, objectId, HEX(CAST(objectHash AS CHAR(32) CHARACTER SET utf8)) as objectHash, timestamp, HEX(CAST(timestampAdditionalEntropy AS CHAR(6) CHARACTER SET utf8))
from ledger
order by timestamp desc, timestampAdditionalEntropy desc;
I need to get this:
# objectOwnerId, objectId, objectHash, timestamp, HEX(CAST(s.timestampAdditionalEntropy AS CHAR(6) CHARACTER SET utf8))
#owneraaaaa, ida, 0D84F725ACAC87838C34742CA00BBEF7, 2019-05-29 11:38:12.350648, 41E425
#owneraaaab, idb, A8D3B63EFC6C63FD996B8D1931FBF748, 2019-05-29 11:38:12.353521, 725E3D
which this query can return:
select s.objectOwnerId, s.objectId, HEX(CAST(objectHash AS CHAR(32) CHARACTER SET utf8)) as objectHash, s.timestamp, HEX(CAST(s.timestampAdditionalEntropy AS CHAR(6) CHARACTER SET utf8)) from (
select s.objectOwnerId, s.objectId, s.timestamp, max(i.timestampAdditionalEntropy) as timestampAdditionalEntropy from (
select objectOwnerId, objectId, max(timestamp) as timestamp
from ledger where ((objectOwnerId = 'owneraaaaa' AND objectId = 'ida') OR (objectOwnerId = 'owneraaaab' AND objectId = 'idb'))
group by objectOwnerId, objectId
) s
JOIN ledger i on i.objectOwnerId = s.objectOwnerId and i.objectId = s.objectId and i.timestamp = s.timestamp
group by objectOwnerId, objectId, timestamp
) s
JOIN ledger i on i.objectOwnerId = s.objectOwnerId and i.objectId = s.objectId and i.timestamp = s.timestamp and i.timestampAdditionalEntropy = s.timestampAdditionalEntropy
I'm new to MySQL and as part of an exercise I have had to create a database for a flight reservation system, I'm trying to create a customer which means inserting a row into 3 linked tables. I'm trying to do this as a transaction but my syntax seems very clunky. Is there a better way to do this?
Here's my transaction (which does work) but I feel there must be a FAR more elegant way to do this:
START TRANSACTION;
SET #account_id = NULL;
INSERT INTO ezy_account (account_id, username, password)
VALUES (#account_id = #account_id, 'customer#mysite.com',AES_ENCRYPT('password1', 'encryptionkey'));
SET #payment_id = NULL;
INSERT INTO ezy_payment_details (payment_details_id, payment_type, account_id, card_number, name, valid_from, expiry)
VALUES (#payment_id = #payment_id, 1, (SELECT account_id FROM ezy_account WHERE username ='ngray#mysite.com'), '1234567890123456', 'chris cust', '2018-03-00', '2021-10-00');
INSERT INTO ezy_customer (customer_id, payment_details, title, first_name, last_name, email_address, address, address_continued, city, postcode, country, dialling_code, phone_number, account_id, easyjet_plus_number, preferred_airport1, preferred_airport2, preferred_airport3)
VALUES (NULL, (SELECT payment_details_id FROM ezy_payment_details WHERE card_number ='1234567890123456'), 1, 'chris', 'customer', 'customer#nmysite.com', '123 Road Street', NULL, 'london', 'SE1 4HG', 7, 7, '1898765432', (SELECT account_id FROM ezy_account WHERE username ='customer#mysite.com'), NULL, NULL, NULL, NULL);
COMMIT;
How can I express the below statement as a SQL query ?
IF EXISTS (SELECT * FROM expense_history
WHERE user_id = 40
AND DATE_FORMAT(expense_history.created_date , '%Y-%m-%d') = '2018-06-02'
AND camp_id='80')
UPDATE expense_history
SET clicks = clicks + 1,
amount = amount + 1
WHERE user_id = 40
AND DATE_FORMAT(expense_history.created_date, '%Y-%m-%d') = '2018-06-02'
AND camp_id = '80'
ELSE
INSERT INTO expense_history (camp_id, created_date, amount, user_id)
VALUES ('80', '2018-06-02 12:12:12', '1', '40')
END IF;
I just want to do increment clicks and amount if is set by day, else I want to add new row.
This is very tricky in MySQL. You are storing a datetime but you want the date part to be unique.
Starting in MySQL 5.7.?, you can use computed columns for the unique constraint. Here is an example:
create table expense_history (
user_id int,
camp_id int,
amount int default 0,
clicks int default 1,
. . .
created_datetime datetime, -- note I changed the name
created_date date generated always as (date(created_datetime)),
unique (user_id, camp_id, created_datetime)
);
You can then do the work as:
INSERT INTO expense_history (camp_id, created_datetime, amount, user_id)
VALUES (80, '2018-06-02 12:12:12', 1, 40)
ON DUPLICATE KEY UPDATE
amount = COALESCE(amount + 1, 1),
clicks = COALESCE(clicks + 1, 1);
Earlier versions of MySQL don't support generated columns. Nor do they support functions on unique. But you can use a trick on a prefix index on a varchar to do what you want:
create table expense_history (
user_id int,
camp_id int,
amount int default 0,
clicks int default 1,
. . .
created_datetime varchar(19),
unique (created_datetime(10))
);
This has the same effect.
Another alternative is to store the date and the time in separate columns.
I presumed your database is mysql, because of DATE_FORMAT() function(and edited your question as to be).
So, by using such a mechanism below, you can do what you want,
provided that a COMPOSITE PRIMARY KEY for camp_id, amount, user_id columns :
SET #camp_id = 80,
#amount = 1,
#user_id = 40,
#created_date = sysdate();
INSERT INTO expense_history(camp_id,created_date,amount,user_id,clicks)
VALUES(#camp_id,#created_date,#amount,#user_id,ifnull(clicks,1))
ON DUPLICATE KEY UPDATE
amount = #amount + 1,
clicks = ifnull(clicks,0)+1;
SQL Fiddle Demo
Anyone see why the query below would yield the error
"#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '%s)"?
SELECT SQL_CALC_FOUND_ROWS id
FROM (
SELECT taba.id
FROM (
SELECT alum.id
FROM cvm_education AS edu
JOIN cvm_alumni AS alum ON alum.id = edu.alumni_id
WHERE cvm_alumni.profile_status =1
AND highest_edu
IN (
SELECT name
FROM cvm_filter_educationlevels
JOIN cvm_educationlevel AS edulevels ON educationlevel_id = edulevels.id
WHERE filter_id = % s
)
) AS taba
Cheers!
you need to quote the string value and use LIKE for pattern matching
WHERE filter_id LIKE '% s'
but if you really want to find % s literally, use =
WHERE filter_id = '% s'
Try this:
SELECT count(taba.id)
FROM (
SELECT alum.id
FROM cvm_education AS edu
JOIN cvm_alumni AS alum ON alum.id = edu.alumni_id
WHERE alum.profile_status =1
AND highest_edu
IN (
SELECT name
FROM cvm_filter_educationlevels
JOIN cvm_educationlevel AS edulevels ON educationlevel_id = edulevels.id
WHERE filter_id = 1
)
) AS taba ;
http://www.sqlfiddle.com/#!2/f8adc/15
Two important points:
I don't understand the use of SQL_CALC_FOUND_ROWS() if have chanced
it in count(). I think this provides the same desired result.
You haven't provided sample data so I wasn't able to try %s. I have
substituted it with a binary (1,0). Furthermore, I don't know your
exact code so I made some assumptions based on your query.
Sample data:
CREATE TABLE cvm_education(
ID int auto_increment primary key,
alumni_id int
);
CREATE TABLE cvm_alumni(
ID int auto_increment primary key,
profile_status int,
highest_edu varchar(30)
);
CREATE TABLE cvm_filter_educationlevels (
ID int auto_increment primary key,
educationlevel_id int,
name varchar(30)
);
CREATE TABLE cvm_educationlevel(
ID int auto_increment primary key,
filter_id int
);
INSERT INTO cvm_education (alumni_id)
VALUES (10), (1), (2), (3),(5), (6),(7),(8),(9);
INSERT INTO cvm_alumni (profile_status, highest_edu)
VALUES (1, "master"),
(0,"bachelor"),
(1,"bachelor"),
(0, "master"),
(1, "master"),
(0, "master"),
(1, "master"),
(1, "master"),
(1, "master"),
(1, "master");
INSERT INTO cvm_filter_educationlevels(educationlevel_id,name)
VALUES (1, "master"), (0,"bachelor");
INSERT INTO cvm_educationlevel(filter_ID)
VALUES (1), (0), (1), (0), (0), (1),(1),(1),(1);
The "% s" is invalid syntax. If that's a literal, then it needs to be enclosed in quotes:
WHERE filter_id = '% s'
(But that fix doesn't appear to be right. It almost looks as if the MySQL statement is being generated with a sprintf, and there was intended to be a '%s' placeholder that was supposed to be replaced with an value.)
Also, there's a closing parenthesis and alias missing from the end of the statement:
) foo
And this:
WHERE cvm_alumni.profile_status = 1
should be changed to this:
WHERE alum.profile_status = 1
(The table is assigned an alias, the column reference should be qualified with the alias, not the table_name)
It's also a good idea to qualify the references all column references, including educationlevel_id, highest_edu and name. (That's not necessarily a problem with the statement, unless MySQL is throwing an "ambiguous column" error, but I prefer to insulate my statements from any "ambiguous column" error that will crop up when new columns are added.)
SELECT SQL_CALC_FOUND_ROWS id
FROM (SELECT taba.id
FROM (
SELECT alum.id
FROM cvm_education edu
JOIN cvm_alumni alum
ON alum.id = edu.alumni_id
WHERE alum.profile_status = 1
AND `highest_edu` IN
(
SELECT `name`
FROM cvm_filter_educationlevels
JOIN cvm_educationlevel edulevels
ON `educationlevel_id` = edulevels.id
WHERE `filter_id` = '% s'
)
) taba
) foo