SQL Query Resource - An Easier way? - mysql

I am looking for some ideas or help on how I could make my query a little less resource intensive but I am not sure if there is an easier way to do what I am doing.
I have a search Store Proc that takes in a large number of parameters to search the database for different products. The user can search for multiple makes and models etc in the same search.
The way I work the search is the parameters are all organised and the call of the store procedure is produced and saved in the data base this is then called and the store procedure is executed and the results returned.
An example:
Make Ids selected : 1 ,2 ,3 ,4
Model IDs selected : 2,3,4
Proc call generated "Call my_store_prod ('1,2,3,4','2,3,4')"
This is saved in the database with a random string eg "wmF14ndfglq2p3yuMMM6cr" then this is used to call the store proc using $_Get['search_id'] www.site.com?search=wmF14ndfglq2p3yuMMM6cr
In the Store proc I then create some temp tables:
DROP TEMPORARY TABLE IF EXISTS tmp_manufacturer_id;
CREATE TEMPORARY TABLE tmp_manufacturer_id (manufacturer_id varchar(255));
/*MODEL_ID*/
DROP TEMPORARY TABLE IF EXISTS tmp_model_id;
CREATE TEMPORARY TABLE tmp_model_id (model_id varchar(255));
Then use a funtion to split the string of ids '1,2,3,4' and insert into the temp tables:
CALL sp_split(#manufacturer_id,'tmp_manufacturer_id');
/*MODEL_ID*/
CALL sp_split(#model_id,'tmp_model_id') ;
Then in the where clause I have something like this:
CASE WHEN #manufacturer_id = ''
THEN information_view.advert_id = information_view.advert_id
ELSE information_view.manufacturer_id IN (select * from tmp_manufacturer_id2)end
AND
CASE WHEN #model_id = ''
THEN information_view.advert_id = information_view.advert_id
ELSE information_view.model_id IN (select * from tmp_model_id2)end
This all is working fine but one of my problems is I need to return 7 Random "Featured" Adverts with the same search results.
The only that I have thought of is to do a UNION with the same query only with a
order by rand()
LIMIT 7
I was wondering if there would be an easier way to get my 7 random adverts without having to duplicate the whole query?
Any pointers for be really useful.
Server version: 5.7.37-log - MySQL Community Server (GPL)

Related

MySQL temporary tables do not clear

Background - I have a DB created from a single large flat file. Instead of creating a single large table with 106 columns. I created a "columns" table which stores the column names and the id of the table that holds that data, plus 106 other tables to store the data for each column. Since not all the records have data in all columns, I thought this might be a more efficient way to load the data (maybe a bad idea).
The difficulty with this was rebuilding a single record from this structure. To facilitate this I created the following procedure:
DROP PROCEDURE IF EXISTS `col_val`;
delimiter $$
CREATE PROCEDURE `col_val`(IN id INT)
BEGIN
DROP TEMPORARY TABLE IF EXISTS tmp_record;
CREATE TEMPORARY TABLE tmp_record (id INT(11), val varchar(100)) ENGINE=MEMORY;
SET #ctr = 1;
SET #valsql = '';
WHILE (#ctr < 107) DO
SET #valsql = CONCAT('INSERT INTO tmp_record SELECT ',#ctr,', value FROM col',#ctr,' WHERE recordID = ',#id,';');
PREPARE s1 FROM #valsql;
EXECUTE s1;
DEALLOCATE PREPARE s1;
SET #ctr = #ctr+1;
END WHILE;
END$$
DELIMITER ;
Then I use the following SQL where the stored procedure parameter is the id of the record I want.
CALL col_val(10);
SELECT c.`name`, t.`val`
FROM `columns` c INNER JOIN tmp_record t ON c.ID = t.id
Problem - The first time I run this it works great. However, each subsequent run returns the exact same record even though the parameter is changed. How does this persist even when the stored procedure should be dropping and re-creating the temp table?
I might be re-thinking the whole design and going back to a single table, but the problem illustrates something I would like to understand.
Unsure if it matters but I'm running MySQL 5.6 (64 bit) on Windows 7 and executing the SQL via MySQL Workbench v5.2.47 CE.
Thanks,
In MySQL stored procedures, don't put an # symbol in front of local variables (input parameters or locally declared variables). The #id you used refers to a user variable, which is kind of like a global variable for the session you're invoking the procedure from.
In other words, #id is a different variable from id.
That's the explanation of the immediate problem you're having. However, I would not design the tables as you have done.
Since not all the records have data in all columns, I thought this might be a more efficient way to load the data
I recommend using a conventional single table, and use NULL to signify missing data.

MySQL views with arguments

I have been working on a pretty large database this last week. Basically I am taking an Access database and converting it to a MySQL database. I have seccessfully converted all the tables and views to MySQL. However I have a view that requires input from the user, the date. The other view is the view that will be call.
view 1 - compiled_fourweeks - needs date
view 2 - metrics_fourweeks - uses `compiled_fourweeks in query.
I was thinking of a precedure but I won't be able to reference the columns in the query.
I am kind of running out of ideas at this point.
If I understand correctly, you need to execute a view (metrics_fourweeks) that needs data from another view (compiled_fourweeks), and this last view requires input from the user.
I would go with the procedure approach:
create procedure fourWeeksData(d date)
create or replace view compiled_fourweeks
select ...
from ...
where recordDate = f -- Just an example; use whichever where clause you need
...;
select * from metrics_fourweeks;
end
If your database will be used just by a single user, your problem is solved. But if your database is meant to be used by more than one user... well, you can use temporary tables:
create procedure fourWeeksData2(d date)
drop table if exists temp_compiled_fourweeks;
create temporary table temp_compiled_fourweeks
select ...
from ...
where recordDate = f -- Just an example; use whichever where clause you need
...;
-- You will need to create the required indexes for this new temp table
-- Now replicate the SQL statement, using your new temp table
select ...
from temp_compiled_fourweeks
...;
end
Hope this helps you.

Cannot access temporary tables from within a function

I would like to get count of specific records. So my query will look like the following...
SELECT
ID,
NAME,
(SELECT...) AS UserCount // Stmt1
FROM MyTable
The issue is that, 'Stmt1' is a complex statement and it cannot be written as innerquery.
Well, I can use functions, but the statement includes 'CREATE TABLE' so I get the following error message
Cannot access temporary tables from within a function.
What is the best way to accomplish the task ?
You can use user defined table type to solve your problem.
You just create a table variable like
CREATE TYPE [dbo].[yourTypeName] AS TABLE(
[columeName1] [int] NULL,
[columeName2] [varchar](500) NULL,
[columeName3] [varchar](1000) NULL
)
GO
and you can declare this table variable in your function like
CREATE FUNCTION [dbo].[yourFunctionName]
(
#fnVariable1 INT ,
#yourTypeNameVariable yourTypeName READONLY
)
RETURNS VARCHAR(8000)
AS
BEGIN
SELECT .................
FROM #yourTypeNameVariable
WHERE ........
RETURN #r
END
On your procedure you can declare your table type like
DECLARE #yourTypeNamevaribale AS yourTypeName
And you can insert values to this table like
insert into #yourTypeNamevaribale (col,col,..)values(val,val,..)
pass this to your function like
dbo.yourFunctionName(fnVariable1 ,#yourTypeNamevaribale )
please go for this method, thank you
Yes you can not use #temp table.
As you are using SQL Server 2008, why don't you use table variable instead of #temp tables?
Give it a try.
I came across this post as I started using table variables and switched to temporary tables for performance reasons only to find temporary tables couldn't be used in a function.
I would be hesitant about using table variables especially if you are playing with large result sets, as these are held in memory. See this post...
http://totogamboa.com/2010/12/03/speed-matters-subquery-vs-table-variable-vs-temporary-table/
Other alternatives would be..
Extracting the temporary table result into another table function.
Converting the code into using sub-queries
In 99,99% of cases there is no need for any tricks with temp tables or subqueries, but use aggregation functions like COUNT, SUM or AVG in combination with OVER clause and (often) PARTITION BY.
I am not sure what the OP tried to achieve but I assume that the UserCount is somehow related to the values in MyTable. So there must be a way to join MyTable to whatever table that produces UserCount.
The most simple example is to show all users and the total number of users
SELECT id
, name
, user_count = COUNT(*) OVER()
FROM MyUsers

Call a Stored Procedure From a Stored Procedure and/or using COUNT

Ok, First off, I am not a mysql guru. Second, I did search, but saw nothing relevant related to mysql, and since my DB knowledge is limited, guessing syntactical differences between two different Database types just isn't in the cards.
I am trying to determine if a particular value already exists in a table before inserting a row. I've decided to go about this using two Stored procedures. The first:
CREATE PROCEDURE `nExists` ( n VARCHAR(255) ) BEGIN
SELECT COUNT(*) FROM (SELECT * FROM Users WHERE username=n) as T;
END
And The Second:
CREATE PROCEDURE `createUser` ( n VARCHAR(255) ) BEGIN
IF (nExists(n) = 0) THEN
INSERT INTO Users...
END IF;
END
So, as you can see, I'm attempting to call nExists from createUser. I get the error that no Function exists with the name nExists...because it's a stored procedure. I'm not clear on what the difference is, or why such a difference would be necessary, but I'm a Java dev, so maybe I'm missing some grand DB-related concept here.
Could you guys help me out by any chance?
Thanks
I'm not sure how it helped you, but...
why SELECT COUNT(*) FROM (SELECT * FROM Users WHERE username=n) and not just SELECT COUNT(*) FROM Users WHERE username=n?
Just make the user name (or whatever the primary application index is) a UNIQUE index and then there is no need to test: Just try to insert a new record. If it already exists, handle the error. If it succeeds, all is well.
It can (and should) all be one stored procedure.

How to call a stored procedure and alter the database table in 1 go

I'm really struggeling with this for some time now.
I have a MySQL database and a lot of data. It is a formula1 website i have to create for college.
Right now the j_tracks_rounds_results table is filled with data but one column is not filled out. It's the rank column.
I created a stored procedure as the following:
DROP PROCEDURE `sp_rank`//
delimiter ##
PROCEDURE `sp_rank`(sid INT)
begin
set #i = 0;
SELECT `performance`, subround_id, #i:=#i+1 as rank
FROM `j_tracks_rounds_results`
where subround_id = sid
order by `subround_id`,`performance`;
end
delimiter ;
The output is like the following:
rec.ID PERFORMANCE SUBROUND_ID RANK
87766 100,349114516829 1 1
93040 101,075635087628 1 2
88851 101,664302543497 1 3
It gets the results and ads a rank to it, sorted on performance so the lowest performance gets rank1 etc...
What i am trying to achieve is to put the rank back into the table. Like an ALTER command for the column "rank".
How would i be able to accomplish this?
Basically don't...
Create table to hold the key (rec.id ?) and the rank. Truncate it to get rid of the previous results then use insert into ... with your query and then join to it.
You really don't want to be altering tables in your normal running, guaranteed some one will use the column when it isn't there, and then when you look at the fault it will be...
People just don't look for table structures changing through the application lifetime, it's a screw up waiting to happen.
You are misapplying your SQL statements. You want the UPDATE command, not ALTER.
eg.
UPDATE table SET rank=#i WHERE subround_id=#id