should I use MySQL procedure for this query? - mysql

I want to make stored procedure but I don't know what should be the right approach or if this is even possible to do in MySQL.
Let me introduce you with my problem. Let's say I have table that has columns like this:
TABLE A
id | Hotel | city_name | region_name | country
1 | A | Amsterdam | North-Holland | Netherlands
2 | B | Amsterdam | North-Holland | Netherlands
3 | C | Leiden | North-Holland | Netherlands
4 | D | Katwijk | North-Holland | Netherlands
5 | E | Leiden | North-Holland | Netherlands
6 | F | Katwijk | North-Holland | Netherlands
I would like to get only 3 results each time I execute this query and results need to be created in this order:
If there are 3 or more cities ( user selects in this case we can take Amsterdam ) that city_name=Amsterdam then return random 3 cities in Amsterdam
If there is less then 3 records in Amsterdam return any of the record that has Amsterdam + return random records where region=North-Holland but the total amount of the records that are returned should always be 3 ( example: we have 2 records where city= Amsterdam + we take one random record from region=North Holand;
example2: we have one record where city=Amsterdam + we take 2 random records where region="north Holland")
Is that possible to this with the SQL Or should I get all the records in php and then iterate through each of the records?
I probably need to pass 2 arguments in the procedure (city_name, region ).
So far I have tried some basic SQL queries and I couldn't get it to work.

You should try something like
(SELECT *
FROM A
WHERE city_name = 'Amsterdam'
ORDER BY RAND()
LIMIT 3)
UNION
(SELECT *
FROM A
WHERE region = 'North Holand' AND city_name <> 'Amsterdam'
ORDER BY RAND()
LIMIT 3)
LIMIT 3;

I would recommend you do the logical part of this primarily in php. While it is possible to do in sql, I've found that logic structures in sql tend to be hard to follow, and that is less of an issue in php.
Doing the logic in php could require two separate queries (but only if you don't get 3 initially).
I'd run a query to get the initial three (use LIMIT 3 in the sql). Check to see if you got three results. If you didn't, subtract the amount you do get from 3, then use that as the LIMIT in a second query to get the other random results.

Related

Need help on SQL query - Select data with duplicate multiple data entries in one data field

I need to select all data having non-duplicate IDs..
here's my sample table..
----------------------------------------------------------------------------------
ID | Zip-Code | Search Query | ID_LIST
----------------------------------------------------------------------------------
1 | 1000 | Query Sample 1 | 13,14,15,
----------------------------------------------------------------------------------
2 | 2000 | Query Sample 2 | 16,13,17,
----------------------------------------------------------------------------------
3 | 3000 | Query Sample 3 | 18,17,13,
----------------------------------------------------------------------------------
4 | 4000 | Query Sample 4 | 15,16,17,18,
----------------------------------------------------------------------------------
5 | 5000 | Query Sample 5 | 19, 20,
u can notice that IDs 1 and 2 have duplicate, which is 13 on ID_LIST
2 and 3 also have duplicate, which is 13 and 17.
What I want to do is make it like this...
----------------------------------------------------------------------------------
ID | Zip-Code | Search Query | ID_LIST
----------------------------------------------------------------------------------
1 | 1000 | Query Sample 1 | 13,14,15,
----------------------------------------------------------------------------------
2 | 2000 | Query Sample 2 | 16,17,
----------------------------------------------------------------------------------
3 | 3000 | Query Sample 3 | 18,
----------------------------------------------------------------------------------
5 | 5000 | Query Sample 5 | 19,20,
What query would be good for this? Any Help?
Best way to approach it is to normalize your data, as mentioned in comments. But if you absolutely have to do it this way, it would be very difficult to do in query on mysql.
I would suggest you to create a procedure for it. As and when you develop each step, you can google that particular solution of that step, and test it and build up on that. Let me know if any step sound confusing/unclear.
Create a variable string, say v_vals. Initialize with null. At the end of procedure, it will contain all the distinct values of id_list (13,14...20)
Iterate through each row.
Count the number of comma in id_list.
Loop from 1 to number of comma.
In every iteration, use substring and instring to find position of each comma and then extract values from id_list. (13,14...)
use another variable v_id_list. Put null in it.
Search for the values (from step 5) in v_vals. If they exist in v_val, then skip them, else put them in v_val and v_id_list.
Now run an update statement to update id_list with v_id_list.
Now repeat Step 3 to 8 for each row.
Note that v_id_list will be reinitialize for each loop, however v_val will contain all the distinct values of id_list.

INSERT data from one table INTO another with the copies (as many as `quantity` field in first table says)

I have an MySQL table creatures:
id | name | base_hp | quantity
--------------------------------
1 | goblin | 5 | 2
2 | elf | 10 | 1
And I want to create creature_instances based on it:
id | name | actual_hp
------------------------
1 | goblin | 5
2 | goblin | 5
3 | elf | 10
The ids of creatures_instances are not important and not relevant to creatures.ids.
How can I make it with just the MySQL in the most optimal (in terms of execution time) way? The single query would be best, but procedure is ok too. I use InnoDB.
I know that with a help of e.g. php I could:
select each row separately,
make for($i=0; $i<line->quantity; $i++) loop in which I insert one row to creatures_instances for each iteration.
The most efficient way is to do everything in SQL. It helps if you have a numbers table. Without one, you can generate the numbers in a subquery. The following works up to 4 copies:
insert into creatures_instances(id, name, actual_hp)
select id, name, base_hp
from creatures c join
(select 1 as n union all select 2 union all select 3 union all select 4
) n
on n.n <= c.quantity;

MySQL - COUNT before INSERT in one query

Hey all, I am looking for a way to query my database table only once in order to add an item and also to check what last item count was so that i can use the next number.
strSQL = "SELECT * FROM productr"
After that code above, i add a few product values to a record like so:
ID | Product | Price | Description | Qty | DateSold | gcCode
--------------------------------------------------------------------------
5 | The Name 1 | 5.22 | Description 1 | 2 | 09/15/10 | na
6 | The Name 2 | 15.55 | Description 2 | 1 | 09/15/10 | 05648755
7 | The Name 3 | 1.10 | Description 3 | 1 | 09/15/10 | na
8 | The Name 4 | 0.24 | Description 4 | 21 | 09/15/10 | 658140
i need to count how many times it sees gcCode <> 'na' so that i can add a 1 so it will be unique. Currently i do not know how to do this without opening another database inside this one and doing something like this:
strSQL2 = "SELECT COUNT(gcCode) as gcCount FROM productr WHERE gcCode <> 'na'
But like i said above, i do not want to have to open another database query just to get a count.
Any help would be great! Thanks! :o)
There's no need to do everything in one query. If you're using InnoDB as a storage engine, you could wrap your COUNT query and your INSERT command in a single transaction to guarantee atomicity.
In addition, you should probably use NULL instead of na for fields with unknown or missing values.
They're two queries; one is a subset of the other which means getting what you want in a single query will be a hack I don't recommend:
SELECT p.*,
(SELECT COUNT(*)
FROM PRODUCTR
WHERE gccode != 'na') AS gcCount
FROM PRODUCTR p
This will return all the rows, as it did previously. But it will include an additional column, repeating the gcCount value for every row returned. It works, but it's redundant data...

Combine count rows in MySQL

I've got a table in MySQL that looks roughly like:
value | count
-------------
Fred | 7
FRED | 1
Roger | 3
roger | 1
That is, it was created with string ops outside of MySQL, so the values are case- and trailing-whitespace-sensitive.
I want it to look like:
value | count
-------------
Fred | 8
Roger | 4
That is, managed by MySQL, with value a primary key. It's not important which one (of "Fred" or "FRED") is kept.
I know how to do this in code. I also know how to generate a list of problem values (with a self-join). But I'd like to come up with a SQL update/delete to migrate my table, and I can't think of anything.
If I knew that no pair of records had variants of one value, with the same count (like ("Fred",4) and ("FRED",4)), then I think I can do it with a self-join to copy the counts, and then an update to remove the zeros. But I have no such guarantee.
Is there something simple I'm missing, or is this one of those cases where you just write a short function outside of the database?
Thanks!
As an example of how to obtain the results you are looking for with a SQL query alone:
SELECT UPPER(value) AS name, SUM(count) AS qty FROM table GROUP BY name;
If you make a new table to hold the correct values, you INSERT the above query to populate the new table as so:
INSERT INTO newtable (SELECT UPPER(value) AS name, SUM(count) AS qty FROM table GROUP BY name);
Strangely, MySQL seems to do this for you. I just tested this in MySQL 5.1.47:
create table c (value varchar(10), count int);
insert into c values ('Fred',7), ('FRED',1), ('Roger',3), ('roger',1);
select * from c;
+-------+-------+
| value | count |
+-------+-------+
| Fred | 7 |
| FRED | 1 |
| Roger | 3 |
| roger | 1 |
+-------+-------+
select value, sum(count) from c group by value;
+-------+------------+
| value | sum(count) |
+-------+------------+
| Fred | 8 |
| Roger | 4 |
+-------+------------+
I was surprised to see MySQL transform the strings like that, and I'm not sure I can explain why it did that. I was expecting to have to get four distinct rows, and to have to use some string functions to map the values to a canonical form.

How can I use rows in a lookup table as columns in a MySQL query?

I'm trying to build a MySQL query that uses the rows in a lookup table as the columns in my result set.
LookupTable
id | AnalysisString
1 | color
2 | size
3 | weight
4 | speed
ScoreTable
id | lookupID | score | customerID
1 | 1 | A | 1
2 | 2 | C | 1
3 | 4 | B | 1
4 | 2 | A | 2
5 | 3 | A | 2
6 | 1 | A | 3
7 | 2 | F | 3
I'd like a query that would use the relevant lookupTable rows as columns in a query so that I can get a result like this:
customerID | color | size | weight | speed
1 A C D
2 A A
3 A F
The kicker of the problem is that there may be additional rows added to the LookupTable and the query should be dynamic and not have the Lookup IDs hardcoded. That is, this will work:
SELECT st.customerID,
(SELECT st1.score FROM ScoreTable st1 WHERE lookupID=1 AND st.customerID = st1.customerID) AS color,
(SELECT st1.score FROM ScoreTable st1 WHERE lookupID=2 AND st.customerID = st1.customerID) AS size,
(SELECT st1.score FROM ScoreTable st1 WHERE lookupID=3 AND st.customerID = st1.customerID) AS weight,
(SELECT st1.score FROM ScoreTable st1 WHERE lookupID=4 AND st.customerID = st1.customerID) AS speed
FROM ScoreTable st
GROUP BY st.customerID
Until there is a fifth row added to the LookupTable . . .
Perhaps I'm breaking the whole relational model and will have to resolve this in the backend PHP code?
Thanks for pointers/guidance.
tom
You have architected an EAV database. Prepare for a lot of pain when it comes to maintainability, efficiency and correctness. "This is one of the design anomalies in data modeling." (http://decipherinfosys.wordpress.com/2007/01/29/name-value-pair-design/)
The best solution would be to redesign the database into something more normal.
What you are trying to do is generally referred to as a cross-tabulation, or cross-tab, query. Some DBMSs support cross-tabs directly, but MySQL isn't one of them, AFAIK (there's a blog entry here depicting the arduous process of simulating the effect).
Two options come to mind for dealing with this:
Don't cross-tab at all. Instead, sort the output by row id, then AnalysisString, and generate the tabular output in your programming language.
Generate code on-the-fly in your programming langauge to emit the appropriate query.
Follow the blog I mention above to implement a server-side solution.
Also consider #Marek's answer, which suggests that you might be better off restructuring your schema. The advice is not a given, however. Sometimes, a key-value model is appropriate for the problem at hand.