Asked  7 Months ago    Answers:  5   Viewed   14 times

I have 2 tables in my database. One is for orders, and one is for companies.

Orders has this structure:

OrderID     |     attachedCompanyIDs
------------------------------------
   1                     1,2,3
   2                     2,4

And Company has this structure:

CompanyID      |        name
--------------------------------------
    1                 Company 1
    2                 Another Company
    3                 StackOverflow
    4                 Nothing

To get an order's companies names, I can do a query as such:

SELECT name FROM orders,company
WHERE orderID = 1 AND FIND_IN_SET(companyID, attachedCompanyIDs)

That query works fine, but the following query does not.

SELECT name FROM orders,company
WHERE orderID = 1 AND companyID IN (attachedCompanyIDs)

Why does the first query work but not the second one?

The first query returns:

name
---------------
Company 1
Another Company
StackOverflow

The second query only returns:

name
---------------
Company 1

Why is this, why does the first query return all the companies, but the second query only returns the first one?

 Answers

56
SELECT  name
FROM    orders,company
WHERE   orderID = 1
        AND companyID IN (attachedCompanyIDs)

attachedCompanyIDs is a scalar value which is cast into INT (type of companyID).

The cast only returns numbers up to the first non-digit (a comma in your case).

Thus,

companyID IN ('1,2,3') ? companyID IN (CAST('1,2,3' AS INT)) ? companyID IN (1)

In PostgreSQL, you could cast the string into array (or store it as an array in the first place):

SELECT  name
FROM    orders
JOIN    company
ON      companyID = ANY (('{' | attachedCompanyIDs | '}')::INT[])
WHERE   orderID = 1

and this would even use an index on companyID.

Unfortunately, this does not work in MySQL since the latter does not support arrays.

You may find this article interesting (see #2):

  • 10 things in MySQL (that won’t work as expected)

Update:

If there is some reasonable limit on the number of values in the comma separated lists (say, no more than 5), so you can try to use this query:

SELECT  name
FROM    orders
CROSS JOIN
        (
        SELECT  1 AS pos
        UNION ALL
        SELECT  2 AS pos
        UNION ALL
        SELECT  3 AS pos
        UNION ALL
        SELECT  4 AS pos
        UNION ALL
        SELECT  5 AS pos
        ) q
JOIN    company
ON      companyID = CAST(NULLIF(SUBSTRING_INDEX(attachedCompanyIDs, ',', -pos), SUBSTRING_INDEX(attachedCompanyIDs, ',', 1 - pos)) AS UNSIGNED)
Tuesday, June 1, 2021
 
bancer
answered 7 Months ago
40

You can use DB::raw like as

DB::table('table1')->leftJoin('table2', function($join){
   $join->on(DB::raw("find_in_set(table2.country, table1.fk_country_id)",DB::raw(''),DB::raw('')));
});
Wednesday, March 31, 2021
 
penpen
answered 9 Months ago
94

You do not need to do that. You are using prepared statements, which escape the variables automatically.

Wednesday, March 31, 2021
 
laurent
answered 9 Months ago
81

When you use just "localhost" the MySQL client library tries to use a Unix domain socket for the connection instead of a TCP/IP connection. The error is telling you that the socket, called MySQL, cannot be used to make the connection, probably because it does not exist (error number 2).

From the MySQL Documentation:

On Unix, MySQL programs treat the host name localhost specially, in a way that is likely different from what you expect compared to other network-based programs. For connections to localhost, MySQL programs attempt to connect to the local server by using a Unix socket file. This occurs even if a --port or -P option is given to specify a port number. To ensure that the client makes a TCP/IP connection to the local server, use --host or -h to specify a host name value of 127.0.0.1, or the IP address or name of the local server. You can also specify the connection protocol explicitly, even for localhost, by using the --protocol=TCP option.

There are a few ways to solve this problem.

  1. You can just use TCP/IP instead of the Unix socket. You would do this by using 127.0.0.1 instead of localhost when you connect. The Unix socket might by faster and safer to use, though.
  2. You can change the socket in php.ini: open the MySQL configuration file my.cnf to find where MySQL creates the socket, and set PHP's mysqli.default_socket to that path. On my system it's /var/run/mysqld/mysqld.sock.
  3. Configure the socket directly in the PHP script when opening the connection. For example:

    $db = new MySQLi('localhost', 'kamil', '***', '', 0, 
                                  '/var/run/mysqld/mysqld.sock')
    
Tuesday, June 1, 2021
 
Anand
answered 7 Months ago
51

You need to escape the call to FIND_IN_SET() using quotes:

$query = DB::table('tags_value')
         ->whereRaw('FIND_IN_SET(css,Tags)')
         ->get();

If you want to parameterize the column for which you search in FIND_IN_SET, then you can try something like this:

$colname = 'css'
$query = DB::table('tags_value')
         ->whereRaw('FIND_IN_SET(?,Tags)', [$colname])
         ->get();
Thursday, July 29, 2021
 
astaykov
answered 5 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share