Consolidating information in two rows in MySQL query result - mysql

I have table which looks like this (example)
+----+----------+----------+---------------+
| id | objectId | filterId | filterValueId |
+----+----------+----------+---------------+
| 1 | 55 | 111 | 2345 |
| 2 | 55 | 113 | 2567 |
| 3 | 55 | 113 | 2568 |
| 4 | 58 | 111 | 2347 |
| 5 | 58 | 115 | 2499 |
+----+----------+----------+---------------+
Now, I have query with some LEFT JOINs (main object table, this filter table, meta table and few others). I am creating frontend filtering and I got to the point where it sort of works, but I am stuck at one problem.
When I have two filters in the queue (lets say filterId=>filterValueId 111=>2345 and 113=>2567).
With query
SELECT ojectId IN tableName
WHERE (filterId = 111 AND filterValueId = 2345)
AND (filterId = 113 AND filterValeId = 2567)
I am not able to get out the information, that objectId 55 is matching both these criteria. at least not in single query. It makes sense, I know, but is there a way making MySQL to give such result? (And does this request/procedure have some technical name I could use for googling?)
Or is it best practice to split such filtering into many queries and then intersect results and get out just these which are in every result?
Sorry for using technical terms in wrong way, hopefully sou can understand what I am trying to say :)
Thanks a lot.

Consider the following...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,objectId INT NOT NULL
,filterId INT NOT NULL
,filterValueId INT NOT NULL
);
INSERT INTO my_table VALUES
(1,55,111,2345),
(2,55,113,2567),
(3,55,113,2568),
(4,58,111,2347),
(5,58,115,2499);
SELECT objectid
FROM my_table
WHERE (filterid,filtervalueid) IN ((111,2345),(113,2567))
GROUP
BY objectid
HAVING COUNT(id = 2);
+----------+
| objectid |
+----------+
| 55 |
+----------+

No special name to search in google. You just wrote your fetching sql to only fetch one record instead of two, by using AND. You should use OR.
And correct IN to FROM.
SELECT ojectId
FROM tableName
WHERE
(filterId = 111 AND filterValueId = 2345)
OR (filterId = 113 AND filterValeId = 2567)
Or is it best practice to split such filtering into many queries and
then intersect results and get out just these which are in every
result?
NO! Let database to work hard and good only once. Give it all filters at once. No need to split queries.

Related

Select count with value from different tables

I want to count all entries in one table grouped by the user id.
This is the query I used which works fine.
select uuid_mapping_id, count(*) from t_message group by uuid_mapping_id;
and these are the results:
+-----------------+----------+
| uuid_mapping_id | count(*) |
+-----------------+----------+
| 1 | 65 |
| 4 | 277 |
Now I would like to display the actual user name, instead of the ID.
To achieve this I would need the help of two different tables.
The table t_uuid_mapping which has two columns:
uid_mapping_id, which equals uuid_mapping_id in the other table.
And f_uuid which is also unique but completely different.
f_uuid can also be found in another table t_abook which also contains the names in the column f_name.
The result I am looking for should be:
+-----------------+----------+
| f_name | count(*) |
+-----------------+----------+
| admin | 65 |
| user1 | 277 |
I am new to the database topic and understand that this could be achieved by using JOIN in the query, but to be honest I did not completely understand this yet.
if I understand you correctly:
SELECT tm.f_name, COUNT(*) as count
FROM t_message tm
LEFT JOIN t_abook ta ON (tm.uuid_mapping_id = ta.uid_mapping_id)
GROUP BY tm.f_name

MySQL Many to Many Table Join Slow Performance

I have two tables with a joining column having a Many to Many relationship. There are a few hundred thousand records in each table. I'm seeing some very slow query performance and am having trouble singling out the issue.
Table_A:
+---------------------------+-------------+---------------+
| ID | Name varchar (30) | Age int(3) | Status int(1) |
+----+----------------------+-------------+---------------+
| 1 | Tom | 23 | 1 |
| 2 | Jerry | 34 | 2 |
| 3 | Smith | 21 | 1 |
| 4 | Ben | 46 | 5 |
+---------------------------+-------------+---------------+
Table_B:
+---------------------------+-------------+---------------+
| ID | Name varchar (30) | Sign int(3) | Status int(1) |
+----+----------------------+-------------+---------------+
| 1 | Tom | 12 | 1 |
| 2 | Smith | 8 | 1 |
| 3 | Tom | 3 | 0 |
| 4 | Tom | 10 | 1 |
+---------------------------+-------------+---------------+
I need to get the Age of each Name in Table A who has at least one row in Table B with a match on Name and a Status (Table B) of 1.
I tried:
SELECT Age FROM Table_A
LEFT JOIN Table_B ON Table_A.Name=Table_B.Name
WHERE Table_B.Status=1;
That query takes so long I haven't waited for it to return.
I then tried:
SELECT DISTINCT Age FROM Table_A
LEFT JOIN Table_B ON Table_A.Name=Table_B.Name AND Table_B.Status=1;
That returned very fast.
I tested further and tried:
SELECT DISTINCT Age FROM Table_A
LEFT JOIN Table_B ON Table_A.Name=Table_B.Name
WHERE Table_B.Status=1;
That again didn't return.
I'm confused as to what's going on here.
In the last query shouldn't the WHERE condition act the same as the previous query's JOIN ON condition (Status=1)?
Why does SELECT DISTINCT return results whereas without using DISTINCT the process takes forever?
For a many-to-many table, do not include an AUTO_INCREMENT. Do have the PRIMARY KEY include both other ids. Do have another index. Do use InnoDB.
See More details, plus rationale.
Without seeing an explain plan (or whatever the MySQL equivalent is) it's impossible to say for certain.
My guess would be that the server knows that your OUTER JOIN' to table B is completely irrelevant when you useSELECT DISTINCT, so it just runs against table A and gets the Age values from there without even performing theJOIN. Do you see why theOUTER JOIN` is irrelevant?
In the first query the server needs to perform the JOIN to get the right number of rows back.
When you add the additional logic to your WHERE clause in the last query you've effectively turned it into an INNER JOIN, so now the JOIN has to happen again and it takes a long time.
Make sure you have indexes set on the Table_A.Name, Table_B.Name and Table_B.Status columns
First, you don't need a LEFT JOIN, because you only care about matches:
SELECT a.Age
FROM Table_A a JOIN
Table_B b
ON Table_A.Name = b.Name
WHERE b.Status = 1;
For this query can take advantage of indexes on Table_B(status, name) and Table_A(Name, Age).

mysql query displaying incorrect rows

I have a query which currently should only display one row. However it somehow is displaying 4 rows as its result set even though 1) there are only three rows in the table to begin with 2) only one row matches the query criteria.
I am hoping someone might know what I am doing wrong with this MySql query
My database table structure is as below
smsid (int, auto increment), sms_type (text), sms_status (enum 'pending',sent'),
sms_error (test), sms_message(text), sms_mp3file (varchar 50),
sms_sendon (datetime), send_sms_toid (int 5)
My table entries are as so (following the order of the table columns above)
31 | mp3 | pending | | | helloworld.mp3 | 2013-11-20 16:16:00 | 7
30 | text | sent | | hello test | | 2013-11-18 13:12:00 | 8
29 | voice | sent | | testing 123 | | 2013-11-18 10:05:00 | 18
My query is as below
SELECT sms_messages.*, sms_recipients.cust_profid, sms_recipients.sms_cellnumber,
customer_smsnumbers.sms_number, customer_smsnumbers.sms_number
FROM sms_messages, sms_recipients, customer_smsnumbers
WHERE sms_messages.sms_type='mp3' AND sms_messages.sms_sendon <= '2013-11-21'
AND sms_messages.sms_status='pending' AND
sms_messages.send_sms_toid = sms_recipients.smsuser_id
In your query, you have missed a JOINING clause for customer_smsnumbers table. Similar to sms_messages.send_sms_toid = sms_recipients.smsuser_id you need to have a join clause which either connects sms_messages with customer_smsnumbers table or connects sms_recipients with the customer_smsnumbers table.
In the absence of a join clause other (unintended) records are included in the result.

how to store a *sorted* SELECT result in another table?

In my projects I often need to store the result of a SELECT in another table (we call this a "resultset"). The reason is to dynamically display a large number of rows in a web application while loading only small chunks as necessary.
Typically, this is done by queries such as this one:
SET #counter := 0;
INSERT INTO resultsetdata
SELECT "12345", #counter:=#counter+1, a.ID
FROM sometable a
JOIN bigtable b
WHERE (a.foo = b.bar)
ORDER BY a.whatever DESC;
The fixed "12345" value is just a value to identify the "resultset" as a whole and changes for each query. The second column is a incrementing index counter that is meant to allow direct access to a specific row in the result and the ID column references the specific row in the source data table.
When the application needs a certain range of the result I just join resultsetdata with the source table to get the detailed data - which is quick as opposed to the resultsetdata query above which may take 2-3 seconds to complete (which explains why I need this intermediary table).
The SELECT query itself is not relevant for this question.
resultsetdata has the following structure:
CREATE TABLE `resultsetdata` (
`ID` int(11) NOT NULL,
`ContIdx` int(11) NOT NULL,
`Value` int(11) NOT NULL,
PRIMARY KEY (`ID`,`ContIdx`)
) ENGINE=InnoDB;
This usually works like a charm but lately we noticed that in some cases the ORDER of the result is not correct. This depends on the query itself (for example, adding DISTINCT is a typical cause), the server version and the data contained in the source tables, so I guess one can say that the row order is unpredictable with this method. Probably it depends on internal optimizations.
However, the problem is now that I can't think of any alternative solution that gives me the expected result.
Since the resultset can get several thousands of rows, loading all data in memory and then manually INSERTing it is not feasible.
Any suggestions?
EDIT: For further clarification, have a look at these queries:
DROP TABLE IF EXISTS test;
CREATE TABLE test (ID INT NOT NULL, PRIMARY KEY(ID)) ENGINE=InnoDB;
INSERT INTO test (ID) VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
SET #counter:=0;
SELECT "12345", #counter:=#counter+1, ID
FROM test
ORDER BY ID DESC;
This produces the following result as "expected":
+-------+----------------------+----+
| 12345 | #counter:=#counter+1 | ID |
+-------+----------------------+----+
| 12345 | 1 | 10 |
| 12345 | 2 | 9 |
| 12345 | 3 | 8 |
| 12345 | 4 | 7 |
| 12345 | 5 | 6 |
| 12345 | 6 | 5 |
| 12345 | 7 | 4 |
| 12345 | 8 | 3 |
| 12345 | 9 | 2 |
| 12345 | 10 | 1 |
+-------+----------------------+----+
10 rows in set (0.00 sec)
As said, in some cases (I can't provide a testcase here, sorry), this may lead to a result similar to this:
+-------+----------------------+----+
| 12345 | #counter:=#counter+1 | ID |
+-------+----------------------+----+
| 12345 | 10 | 10 |
| 12345 | 9 | 9 |
| 12345 | 8 | 8 |
| 12345 | 7 | 7 |
| 12345 | 6 | 6 |
| 12345 | 5 | 5 |
| 12345 | 4 | 4 |
| 12345 | 3 | 3 |
| 12345 | 2 | 2 |
| 12345 | 1 | 1 |
+-------+----------------------+----+
I'm not saying this is a MySQL bug and I fully understand that my method currently provides unpredictable results. Still, I don't know how to tweak this to get predictable results.
This is because the order that records are sorted when they are inserted is unrelated to the order when you retrieve them.
When you retrieve them a query plan will be created. If no ORDER BY is specified in your SELECT statement then the order will depend on the query plan produced. This is why it is unpredictable and adding DISTINCT can change the order.
The solution is to store enough data that you can retrieve them in the correct order using an ORDER BY clause. In your case you have ordered your data by a.whatever. Can a.whatever be stored in resultsetdata? If so then you can read the records out in the correct order.
Maybe you could wrap the select into another select:
SET #counter := 0;
INSERT INTO resultsetdata
SELECT *, #counter := #counter + 1
FROM (
SELECT "12345", a.ID
FROM sometable a
JOIN bigtable b
WHERE a.foo = b.bar
ORDER BY a.whatever DESC
) AS tmp
... but you are still at the mercy of the dumbness of MySQL's optimizer.
That's all I found about this topic, but I couln't find a hard guarantee:
Pure-SQL Technique for Auto-Numbering Rows in Result Set
http://www.xaprb.com/blog/2006/12/02/how-to-number-rows-in-mysql/
http://www.xaprb.com/blog/2005/09/27/simulating-the-sql-row_number-function/

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.