MySQL Update if value is x or y - mysql

I have following procedures which help me to update the dates in my table on a daily basis, keeping them relevant for demo purposes. The difference between those two procedures is only that they are being executed for 2 different clients with property_id being 2 and 3 respectively.
UPDATE `opera_pms_detail` SET `arrival` = SUBDATE(curdate(), 2), `departure` = SUBDATE(curdate(), 1), `read_on` = NULL WHERE `property_id` = 2 AND `import_type` = '1'
UPDATE `opera_pms_detail` SET `arrival` = SUBDATE(curdate(), 2), `departure` = SUBDATE(curdate(), 1), `read_on` = NULL WHERE `property_id` = 3 AND `import_type` = '1'
Now, for efficiency's sake, is there a better way of doing this? Instead of having to call the procedure twice, can I just update the columns for both clients in one call?
Thank you

Try implementing your procedure like this
PROCEDURE ýour_procedure_name(IN p_property_id INT)
BEGIN
UPDATE `opera_pms_detail`
SET `arrival` = SUBDATE(curdate(), 2),
`departure` = SUBDATE(curdate(), 1),
`read_on` = NULL
WHERE `property_id` = p_property_id
AND `import_type` = '1';
END
And you will call your procedure as:
CALL `your_procedure_name`(property_id)

Related

How can I add control flow into a mysql function?

I want to optimise a mysql 5.7 function that reads settings from a table. the function returns a 1 or 2 if the date parsed in is in 'semester 1' or 'semester 2'. the dates for semester 1 and 2 change each year.
we have confirmed that dateIn is a valid date.
the function is:
DELIMITER //
CREATE function getSemester (dateIN date)
RETURNS INT DETERMINISTIC
BEGIN
DECLARE sem int;
select if( dateIN < a.mindate,1,2) into sem
from (SELECT min(date(value)) mindate FROM `settings` WHERE name = CONCAT(‘sem2_‘,year(dateIN),‘_start’) ) a;
return sem;
END//
DELIMITER ;
settings is defined as:
CREATE TABLE `settings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`value` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
the settings data is:
INSERT INTO `mdl_sap_settings` (`id`, `name`, `value`)
VALUES
(4, 'sem2_2012_start', '2012/7/16'),
(15, 'sem2_2013_start', '2013/7/1'),
(25, 'sem2_2014_start', '2014/6/30'),
(29, 'sem2_2015_start', '2015/6/29'),
(37, 'sem2_2016_start', '2016/6/27'),
(42, 'sem2_2011_start', '2011/7/16'),
(50, 'sem2_2017_start', '2017/6/26'),
(56, 'sem2_2018_start', '2018/6/25'),
(63, 'sem2_2019_start', '2019/6/24');
the issue is the function is slow quite slow when called on 20,000 rows. I thought to optimise it by having some sort of flow control in the function something like:
if (year(dateIN) = 2012)
{
return dateIN < '2012-07-16' ? 1 : 2;
}
if (year(dateIN) = 2013)
{
return dateIN < '2013-07-01' ? 1 : 2;
}
... etc.
We need to keep the select as is query because if the code is not maintained we want it to return the correct values.
i was just wondering if this sort of control flow is possible in a mysql function, or is there an alternative way to optimise the function?
I don't have 20k records to test with but if I were you, I'd rewrite your function like this and see if that helps.
DELIMITER //
CREATE function getSemester (dateIN date)
RETURNS INT DETERMINISTIC
BEGIN
DECLARE sem int;
DECLARE nameByDate varchar(255);
SET nameByDate = (SELECT CONCAT('sem2_',year(dateIN),'_start') );
select if( dateIN < a.mindate,1,2) into sem
from (SELECT min(date(value)) mindate FROM `test`.`settings` WHERE name = nameByDate ) a;
return sem;
END//
DELIMITER ;
I'd avoid adding a concat in the where clause as there is a performance hit on large dataset. So I moved it out of where clause and assigned it to a variable once. If that doesn't help you can also try the query with explain and see if that provides any hints.
EXPLAIN select getSemester('2019/6/24');
Good luck.
With 20,000 rows in the results table, the my query run without the getSemester function in 157 msec.
with the version of getSemster in the question it was taking 1.1 seconds.
with this optimisation the query was running in 0.9 seconds.
DELIMITER //
CREATE function getSemester (dateIN date)
RETURNS tinyint DETERMINISTIC
BEGIN
DECLARE sem tinyint;
DECLARE sem2_start_label char(16);
set sem2_start_label = CONCAT('sem2_',year(dateIN),'_start');
select case year(dateIN)
when 2012 then
case when dateIN < date('2012-07-16') then 1 else 2 end
when 2013 then
case when dateIN < date('2013-07-01') then 1 else 2 end
when 2014 then
case when dateIN < date('2014-06-30') then 1 else 2 end
when 2015 then
case when dateIN < date('2015-06-29') then 1 else 2 end
when 2016 then
case when dateIN < date('2016-06-27') then 1 else 2 end
when 2017 then
case when dateIN < date('2017-06-26') then 1 else 2 end
when 2018 then
case when dateIN < date('2018-06-25') then 1 else 2 end
when 2019 then
case when dateIN < date('2019-06-24') then 1 else 2 end
else
case when dateIN < (SELECT min(date(value)) mindate FROM `settings` WHERE name = sem2_start_label ) then 1 else 2 end
end into sem;
return sem;
END//
DELIMITER ;
what i did was only select the date from the settings table when it was unknown -- seems to give a small optimisation.

MySQL - Update joined column with random values of last 12 digits

I have a MYSQL column called Joined (varchar(50)) with values such as 13 numbers
How can I implement that query ?
UPDATE `users` SET `joined` = 153ZZZZZZZZZZ
Try:
UPDATE `users`
SET `joined` = CONCAT('153', FLOOR(RAND()* 9000000000) + 1000000000)
you could generated md() random string eg:
UPDATE `users`
SET `joined` = concat('153',SUBSTRING(MD5(RAND()) FROM 1 FOR 10) )
or
UPDATE `users`
SET `joined` = concat('153',SUBSTRING(MD5(RAND()) FROM 1 FOR 6),
SUBSTRING(MD5(RAND()) FROM 1 FOR 4) )
or if you need only digit you could use
UPDATE `users`
SET `joined` = concat('153',CAST(rand()*1000000 as UNSIGNED),
CAST(rand()*10000 as UNSIGNED) )
of for rows with short string you could use case when for adapt the code as you need
UPDATE `users`
SET `joined` = case when length(joined)= 14 then
concat('153',CAST(rand()*10000000 as UNSIGNED),
CAST(rand()*10000 as UNSIGNED) )
when length(joined)= 13 then
concat('153',CAST(rand()*1000000 as UNSIGNED),
CAST(rand()*10000 as UNSIGNED) )
.....
end

Column cannot be null - procedure

I am trying to create a procedure in MySQL that insert weeks (for current year) to my week table. But there is a problem because after first row is added for the next one I get an error: number column cannot be null. I am new to MySQL so I will appreciate any help.
CREATE PROCEDURE generateWeeks()
BEGIN
SET #currentYear = YEAR(CURDATE());
SET #nextYear = #currentYear + 1;
SET #startOfCurrentWeek = CURDATE();
WHILE(#currentYear < #nextYear) DO
SET #endOfCurrentWeek = DATE_ADD(#startOfCurrentWeek , INTERVAL 7 DAY);
SET #weekNumber = WEEK(#startOfCurrentWeek, 3) -
WEEK(#startOfCurrentWeek - INTERVAL DAY(#startOfCurrentWeek)-1 DAY, 3) + 1;
INSERT INTO `week` (`number`, `start_date`, `end_date`)
VALUES (#weekNumber, #startOfCurrentWeek, #endOfCurrentWeek);
SET #startOfCurrentWeek = #endOfCurrentWeek + 1;
SET #currentYear = YEAR(#endOfCurrentWeek);
END WHILE;
END //
DELIMITER ;
EDITED:
Table Creation:
CREATE TABLE `week` (
`id` INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`number` INT(11) NOT NULL,
`start_date` DATE NOT NULL,
`end_date` DATE NOT NULL
)
Why for first while iteration everything is ok (rows is added), but in the next one I get null value in #weekNumber variable ?
The line:
SET #startOfCurrentWeek = #endOfCurrentWeek + 1;
will convert the variable into a integer. Use date_add instead.
Also, instead of using user-defined variables (#endOfCurrentWeek) you better use local variabled (declare v_endOfCurrentWeek date).

Update query to reset table field per month basis

I have a table named service_tracker. The table contains 6 fields but the most relevant to my question below is cycle_start and last_used. Currently I am able to determine the following things about a service: the times it has been used (usage_count) by user within 1 month (last_used,cycle_start). First query adds 1 to the usage_count as long as it is not older then 1 month. Second query clears and sets the usage_count = 1 if older than 1 month. Queries work great, however, would it possible to perform this on the basis of the twelve months of the year? Clear and set to 1 the usage_count once April, May, June etc. is over?
Queries:
UPDATE service_tracker
SET usage_count = usage_count + 1,
last_used = CURDATE()
WHERE service_tracker = 'cloudstorage' AND `user` = 'test2' AND last_used >= date_sub(CURDATE(), INTERVAL 1 month);
UPDATE service_tracker
SET usage_count = 1,
last_used = '0000-00-00',
cycle_start = CURDATE()
WHERE service_tracker = 'cloudstorage' AND `user` = 'test2' AND cycle_start < date_sub(CURDATE(), interval 1 month);
Table Schema
CREATE TABLE IF NOT EXISTS `service_tracker` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`service` varchar(50) NOT NULL,
`cycle_start` date NOT NULL,
`user` varchar(200) NOT NULL,
`usage_count` int(6) NOT NULL,
`last_used` date NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `service_tracker` (`id`, `service`, `cycle_start`, `user`, `usage_count`, `last_used`) VALUES
(1, 'webserver', '2015-04-24', 'test1', 13, '2015-04-24'),
(2, 'cloudstorage', '2015-04-16', 'test2', 390, '2015-04-30'),
(3, 'web-traffic-tracker', '2015-04-16', 'test3', 1916, '2015-04-30'),
(4, 'remote-it-help', '2015-04-16', 'test4', 91, '2015-04-16');
You can use the mysql scheduler to run queries at specific (and repeating) times.
DELIMITER $$
CREATE EVENT reset_usage_count
ON SCHEDULE EVERY '1' MONTH
STARTS '2015-05-01 00:00:00'
DO
BEGIN
-- your query to reset the usage_count
END$$
DELIMITER ;
This one would start the query at midnight, on the first of every month. You will need to make sure the global variable event_scheduler is set to on. You can check that it is running by executing show proceslist, and looking for User: event_scheduler

SSRS String -vs- Data Integer

I am working on a SSRS report and using a parameter that allows you to choose multiple options. However, when I do this I get an error that states:
Error Converting Data Type nVarChar to Int.
The data in the database is an Integer. The parameter is set up as an Integer and it works great when only choosing one option. The issue comes when I choose multiple options.
My co-worker came up with one work-around but I would like something a little more elegant and easier to plug in if possible.
Here is his work-around:
ALTER PROCEDURE [dbo].[DtaPrep_MktgClients]
#BegDate date = NULL
, #EndDate date = NULL
, #Species varchar(50) = 'canine,feline,K9,'
 , #HospList varchar(500) = NULL
This is where the hospmastid string gets converted into a temp table
/*
--===================================--
HOSPITALS SETUP
--===================================--
*/
If #HospList IS NOT NULL
BEGIN
DECLARE #WorkHospList varchar(500)
SET #WorkHospList = #HospList
;
CREATE TABLE #HospList
( HospID smallint NULL )
SET #CommaLoc = charindex(',', #WorkHospList)
WHILE #CommaLoc > 1
BEGIN
SET #curVal = LEFT(#WorkHospList, #commaloc-1 )
INSERT INTO
#HospList( HospID )
SELECT #curVal
SET #WorkHospList = substring( #WorkHospList, #commaloc+1, len(#WorkHospList) )
SET #CommaLoc = charindex(',', #WorkHospList)
END
END
This is using the temp table to accomplish the same thing as a “WHERE Hospmastid IN (101,102,103)…”
Method 1
SELECT
HospitalMasterID
, ClientID
, FirstName
, LastName
FROM
Client
WHERE
HospitalMasterID IN (Select HospID From #HospList )
Needless to say, I am sure there is a better way to accomplish this. If anyone has any ideas, please let me know.
Here is the full Query I am now using. But it is not selecting anything so there is an issue with the Created Table.
USE [xxxxx]
GO
/****** Object: StoredProcedure [dbo].[PriceErosion] Script Date: 11/26/2013 8:26:33 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
/*
-- =============================================
-- Author:
-- Create date: 11/25/2013
-- Description: Determines the products in which the price was lowered and revenue lost during a set time period.
-- =============================================
*/
--#StartDate as Date = Null
--,#EndDate as Date = Null
--,#CurDate as Date = Null
--,#Hospital as VarChar = Null
--,#Division as Int = Null
Declare #StartDate as Date = Null
Declare #EndDate as Date = Null
Declare #Hospital as Int = Null
Declare #Division as Int = Null
DECLARE #curDate Date = Null
SET #curDate = GETDATE()
Set #StartDate = CASE WHEN #StartDate IS NULL THEN DATEADD(dd, -31, Dateadd(dd, -1, #curdate) ) ELSE #StartDate END
Set #EndDate = CASE WHEN #EndDate IS NULL THEN Dateadd(dd, -1, #curdate) ELSE #EndDate END
Set #Hospital = Case When #Hospital IS Null Then '3' Else #Hospital End;
IF OBJECT_ID('tempdb.dbo.#HospList') IS NOT NULL DROP TABLE #HospList ;
If #Hospital IS NOT NULL
BEGIN
DECLARE #WorkHospList varchar(500)
Declare #CommaLoc as Int
Declare #curVal as int
SET #WorkHospList = #Hospital
;
CREATE TABLE #HospList
( HospID smallint NULL )
SET #CommaLoc = charindex(',', #WorkHospList)
WHILE #CommaLoc > 1
BEGIN
SET #curVal = LEFT(#WorkHospList, #commaloc-1 )
INSERT INTO
#HospList( HospID )
SELECT #curVal
SET #WorkHospList = substring( #WorkHospList, #commaloc+1, len(#WorkHospList) )
SET #CommaLoc = charindex(',', #WorkHospList)
END
END
Begin
-- Sets the Baseline Price Date in the PriceChangeHistory Table.
With PC1
as
(Select
HospitalMasterID
,TxnCode
,UserInfoMasterID
,Active
,min(TxnDateTime) as StartingDate
From
PriceChangeHistory
Where
TxnDateTime Between #StartDate and #EndDate
Group By
HospitalMasterID, TxnCode, UserInfoMasterID, Active)
-- Gets the Baseline Price for the period from the PriceChangeHistory Table
,PC
as
(Select
PC1.HospitalMasterID
,PC1.TxnCode
,PC1.UserInfoMasterID
,PC1.Active
,Cast (PC1.StartingDate as Date) as StartingDate
,PC2.OldPrice as StartingPrice
,PC2.NewPrice
,PC2.TxnSubType
From
PC1
Inner Join
PriceChangeHistory as PC2
On
PC1.HospitalMasterID = PC2.HospitalMasterID
and
PC1.TxnCode = PC2.TxnCode
and
PC1.StartingDate = PC2.TxnDateTime
Where
PC2.OldPrice > PC2.NewPrice)
--MedicalHistory Information
,MH
as
(Select
HospitalMasterID
,PatientID
,TxnDate
,TxnCode
,Description
,ListAmount
,ExtendedAmount
,TxnType
,Quantity
,(Case
When Quantity <> '1' Then (ListAmount/Quantity)
Else ListAmount
End) as UnitPrice
From
MedicalHistory
Where
TxnDate Between #StartDate and #EndDate
and
_IsServOrITem = 1)
-- Determines the Revenue lost per each sale, also reduces the results to only those items where the Price was lowered not raised.
,RL
as
(Select
PC.HospitalMasterID
,MH.PatientID
,PC.TxnCode
,PC.TxnSubType
,MH.Description
,PC.UserInfoMasterID as ChangedByUserID
,MH.TxnDate
,PC.StartingPrice
,Cast (MH.UnitPrice as Money) as UnitPrice
,Cast ((StartingPrice - UnitPrice) as Money) as RevenueLost
From
PC
Left OUter Join
MH
on
PC.HospitalMasterID = MH.HospitalMasterID
and
PC.TxnCode = MH.TxnCode
Where
PC.StartingPrice > MH.UnitPrice)
--- Determine the name of the tech changing the prices.
,UI
as
(Select
HospitalMasterID
,UserInfoMasterID
,Name
From
UserInfo)
--- Get the Division and Hospital Name for each Hospital.
,HODI
as
(Select
DI.DivisionID
,DI.DivisionName
,HO.HospMastID
,HO.HospCode
,HO.HospName
From
ref_Hospital as HO
inner Join
ref_Division as DI
on
HO.DivisionID = DI.DivisionID)
,HI
as
(Select
HODI.DivisionID
,HODI.DivisionName
,RL.HospitalMasterID
,HODI.HospCode
,HODI.HospName
,RL.PatientID
,RL.TxnCode
,RL.TxnSubType
,RL.Description
,RL.ChangedByUserID
,RL.TxnDate
,RL.StartingPrice
,RL.UnitPrice
,RL.RevenueLost
From
RL
Left Outer Join
HODI
ON
RL.HospitalMasterID = HODI.HospMastID
Where
TXNDate Between #StartDate and #EndDate)
Select
*
From
HI
Where
HospitalMasterID in (Select HospID from #Hosplist)
Order By
HOspitalMasterID
end
Prior to SQL Server 2008, the standard way to filter by one or more values was to pass an XML document to the Stored Procedure and join on it. In this case, you could pass the data as a string with the integers separated by commas, then convert that into an XML document, then join on the XML. So you should change the multiselect in SSRS to a text datatype. Here's a post that shows you how to open an XML document: http://blog.sqlauthority.com/2009/02/13/sql-server-simple-example-of-reading-xml-file-using-t-sql/
SQL Server 2008 lets you use table-valued parameters, but again, it might be best to pass the data as a string of comma separated integers and then let the stored procedure put the data into a table-valued parameter, and then join on that. Here's a post that describes how to use table valued parameters: http://blog.sqlauthority.com/2008/08/31/sql-server-table-valued-parameters-in-sql-server-2008/