Get matching column & its value MySQL - php

Here is my query:
SELECT ID, Name, Description
FROM MyTable
WHERE NAME LIKE :search OR Description LIKE '%search%'
OR Status LIKE '%search%'
OR ... Many Other Columns LIKE '%search%
When I get the data I display it to the user that they searched for. Now I only display ID, Name & Description. I also want to display which column matched the searching string. If it matched Status I want to display the status of the row, if it matched any other column I want to display the value of that column that it matched.
Does not matter if it matches multiple columns. I only need the first one.
Is this possible without selecting all columns & then running PHP search on each to find out which one matched? Can it be done simply in MySQL?
I want to make it as fast as possible since my search is becoming pretty slow the more columns & more tables I add to the search and do not want this feature to add a lot of overhead.

Consider the following...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, a INT NOT NULL, b INT NOT NULL, c INT NOT NULL);
INSERT INTO my_table (a,b,c) VALUES
(101,102,103),
(102,103,104),
(103,104,105),
(104,105,106),
(105,106,107);
SELECT * FROM my_table;
+----+-----+-----+-----+
| id | a | b | c |
+----+-----+-----+-----+
| 1 | 101 | 102 | 103 |
| 2 | 102 | 103 | 104 |
| 3 | 103 | 104 | 105 |
| 4 | 104 | 105 | 106 |
| 5 | 105 | 106 | 107 |
+----+-----+-----+-----+
SELECT id
, CASE WHEN a LIKE '%04%' THEN 'a' WHEN b LIKE '%04%' THEN 'b' WHEN c LIKE '%04%' THEN 'c' END col
, CASE WHEN a LIKE '%04%' THEN a WHEN b LIKE '%04%' THEN b WHEN c LIKE '%04%' THEN c END val
FROM my_table;
+----+------+------+
| id | col | val |
+----+------+------+
| 1 | NULL | NULL |
| 2 | c | 104 |
| 3 | b | 104 |
| 4 | a | 104 |
| 5 | NULL | NULL |
+----+------+------+

You can do it a bit messily like this:-
SELECT ID,
Name,
Description,
CASE
WHEN NAME LIKE :search THEN 'NAME'
WHEN Description LIKE :search THEN 'Description'
WHEN Status LIKE :search THEN 'Status'
... Many Other Columns LIKE :search
ELSE 'Unknown Column'
END AS MatchingColumn
FROM MyTable
WHERE NAME LIKE :search OR Description LIKE :search OR Status LIKE :search OR
... Many Other Columns LIKE :search
Down side is that you are repeating the (fairly time consuming) checks. Not sure if MySQL manages to optimise these away, but it wouldn't surprise me if not.
Alternative might be do a UNION of queries, one for each condition. Then use this as a sub query to select the max from.
Using UNION would give you something like this. It does have the benefit that each UNIONed query can then use indexes (your current query can't as you are checking different columns ORed together to get a match), assuming the LIKE doesn't use a leading wildcard.
SELECT ID, Name, Description, MAX(MatchingColumn)
FROM
(
SELECT ID, Name, Description, 'NAME' AS MatchingColumn
FROM MyTable
WHERE NAME LIKE :search
UNION
SELECT ID, Name, Description, 'Description' AS MatchingColumn
FROM MyTable
WHERE Description LIKE :search
UNION
SELECT ID, Name, Description, 'Status' AS MatchingColumn
FROM MyTable
WHERE Status LIKE :search OR
) sub0
GROUP BY ID, Name, Description

Related

Search query for multiple tables with count(*)?

I have a search field for an online shop that uses MySQL query for 2 different tables. The first table is vinyls which has 13 columns, and the second table is products which has 10 columns. The result I want is that if you enter a keyword, it will search the album_name and artist_name columns of table vinyls, and the name column in table products to find any matches.
My php code first counts the number of times a hit is made, and if it does, uses a query again to display the hits like online stores do. However, when I tested it, it only shows results from the vinyls table and not the products table. I've tried using UNION but because both tables have a different number of columns, it results in an error. I've used NULL to fill up the missing columns needed for union but it doesn't work too. Joins doesn't help either as the two tables do not have any similar column. What complicated it is the use of count(*) for the first query.
My SQL query using UNION and COUNT:
(SELECT COUNT(*) AS numrows
FROM vinyls
WHERE ( album_name LIKE :keyword
OR artist_name LIKE :keyword)
)
union
(SELECT COUNT(*) AS numrows
FROM products
WHERE name LIKE :keyword)
My php code for search field:
$stmt = $conn->prepare("(SELECT COUNT(*) AS numrows
FROM vinyls
WHERE ( album_name LIKE :keyword
OR artist_name LIKE :keyword)
)
union
(SELECT COUNT(*) AS numrows
FROM products
WHERE name LIKE :keyword)");
$stmt->execute(['keyword' => '%'.$_POST['keyword'].'%']);
$row = $stmt->fetch();
if($row['numrows'] < 1){
echo '<h1 class="page-header">No results found for <i>'.$_POST['keyword'].'</i></h1>';
}else{
echo '<h1 class="page-header">Search results for <i>'.$_POST['keyword'].'</i></h1>';
try{
$inc = 3;
$stmt = $conn->prepare("(SELECT *
FROM vinyls
WHERE ( album_name LIKE :keyword
OR artist_name LIKE :keyword)
)
union
(SELECT *
FROM products
WHERE name LIKE :keyword)");
$stmt->execute(['keyword' => '%'.$_POST['keyword'].'%']);
foreach ($stmt as $row) {...
I'm already stumped on how to search the 2 tables from only one search field and display the results from both tables and not only just one table. And it's a similar problem too when adding items to cart as they can be either from the two tables and I have to "join" the two tables again.
Where did I go wrong?
Edit:
Sample data
Vinyls table
+----+------------------+---------------------------+-----------+
| id | album_name | artist_name | genre |
+----+------------------+---------------------------+-----------+
| 11 | Ravel Bolero | Boston Symphony Orchestra | Classical |
| 12 | TV Calendar Show | Arthur Godfrey | Orchestra |
| 13 | Flip Phillips | The Phillips Quartet | Jazz |
| 14 | The Modern Idiom | Various artists | Jazz |
+----+------------------+---------------------------+-----------+
Products table
+----+-------------+------------------------------------------------------+
| id | category_id | name |
+----+-------------+------------------------------------------------------+
| 31 | 15 | Tonka/Diecast trucks and cars |
| 32 | 21 | Plate Number Georgia PQQ 1151 |
| 33 | 6 | Walt Disney Read Along Casette Tapes |
| 34 | 7 | Herbert Von Karajan |
| 35 | 8 | BANK IT! Board Game |
| 36 | 9 | Iraqi Most Wanted Playing Cards |
| 37 | 10 | STAR TREK ( 1991,'92,'93 ) Stamp Oasis Rubber Stamps |
| 38 | 11 | Batman and Friends Action Figure Toys (4" - 5") |
| 39 | 12 | Delta Dawn Porcelain Doll |
+----+-------------+------------------------------------------------------+
Expected result is similar to amazon's search bar, where you type a keyword and results show after you've entered. What I want in my site is that if I type orchestra in the search field, it will display Boston Symphony Orchestra's row. Or if I type cars, it will display the tonka row.
What I get is that it only shows the vinyls table and never the products table even if I type cars and there should have been a hit in the products table. Or I get an error because union is applicable only to tables with similar number of columns.
You have 2 problems in your code.
First, your count query should work and throw no error, but you only look at the first result row (the one of your vinyls). But your query returns two rows. (It will return one row with the numrows field for the first unioned query (vinyls) and one row with the numrows field for the second query (products) in your union.
You could wrap it into a SUM to only get one result row back:
$stmt = $conn->prepare("SELECT SUM(numrows) AS numrows FROM
(SELECT COUNT(*) AS numrows
FROM vinyls
WHERE ( album_name LIKE :keyword
OR artist_name LIKE :keyword)
)
union
(SELECT COUNT(*) AS numrows
FROM products
WHERE name LIKE :keyword)) AS sumQuery");
The second problem is the query, which loads your result and throws the exception because of the nonmatching column count. You have to define which columns of each of the two unioned queries should be returned, and they have to match on both. So f.e. do this:
$stmt = $conn->prepare("(SELECT id, album_name AS name, artist_name
FROM vinyls
WHERE ( album_name LIKE :keyword
OR artist_name LIKE :keyword)
)
union
(SELECT id, name, NULL AS artist_name
FROM products
WHERE name LIKE :keyword)");

Single Query to fetch records from multiple tables with different columns

I have 2 different tables as want to get records in a single query. Currently, I am using 2 queries then merging the array result and then displaying the record. Following is my current code:
$db = JFactory::getDbo();
$query1 = "SELECT a.id as cId, a.title, a.parent_id,a.level FROM `categories` AS a WHERE ( a.title LIKE '%keyword%' )";
$result1 = $db->setQuery($query1)->loadObjectlist(); //gives selected records
$query2 = "SELECT b.id as indId, b.indicator , b.cat_id, b.subcat_id, b.section_id FROM `indicator` as b WHERE ( b.indicator LIKE '%keyword%' )";
$result2 = $db->setQuery($query2)->loadObjectlist(); //gives selected records
$_items = array_merge($result1,$result2); //then using $_items in php code to display the data
It is in Joomla however I just want to know how we can merge these 2 queries into one. I tried the following but it gives the result of first query from categories table.
(SELECT id as cId, title, parent_id,level, NULL FROM `categories` WHERE ( title LIKE '%birth%' ))
UNION ALL
(SELECT id as indId, indicator , cat_id, subcat_id, section_id FROM `indicator` WHERE ( indicator LIKE '%birth%' ))
Desired output:
+------+-------------+------------+--------+--------+----------------+--------+-----------+----------+
| cId | title | parent_id | level | indId | indicator | cat_id | subcat_id | section_id
+------+-------------+------------+--------+--------+----------------+--------+-----------+----------+
| 2874 | births | 2703 | 2 | null | null | null | null | null |
+------+-------------+------------+--------+--------+----------------+--------+-----------+----------+
| 13 | birth weight| 12 | 3 | null | null | null | null | null |
+------+-------------+------------+--------+--------+----------------+--------+-----------+----------+
| null | null | null | null | 135 | resident births| 23 | 25 | 1 |
+------+-------------+------------+--------+--------+----------------+--------+-----------+----------+
| null | null | null | null | 189 | births summary | 23 | 25 | 1 |
+------+-------------+------------+--------+--------+----------------+--------+-----------+----------+
This above output will help to get proper pagination records. I tried to use join but JOIN needs a common column in ON clause. Here, I want all the columns and their values. Basically I want to combine the 2 table records in one query. Any help would be appreciated
Here is an example,
There are a number of ways to do this, depending on what you really want. With no common columns, you need to decide whether you want to introduce a common column or get the product.
Let's say you have the two tables:
parts: custs:
+----+----------+ +-----+------+
| id | desc | | id | name |
+----+----------+ +-----+------+
| 1 | Sprocket | | 100 | Bob |
| 2 | Flange | | 101 | Paul |
+----+----------+ +-----+------+
Forget the actual columns since you'd most likely have a customer/order/part relationship in this case; I've just used those columns to illustrate the ways to do it.
A cartesian product will match every row in the first table with every row in the second:
> select * from parts, custs;
id desc id name
-- ---- --- ----
1 Sprocket 101 Bob
1 Sprocket 102 Paul
2 Flange 101 Bob
2 Flange 102 Paul
That's probably not what you want since 1000 parts and 100 customers would result in 100,000 rows with lots of duplicated information.
Alternatively, you can use a union to just output the data, though not side-by-side (you'll need to make sure column types are compatible between the two selects, either by making the table columns compatible or coercing them in the select):
> select id as pid, desc, '' as cid, '' as name from parts
union
select '' as pid, '' as desc, id as cid, name from custs;
pid desc cid name
--- ---- --- ----
101 Bob
102 Paul
1 Sprocket
2 Flange
In some databases, you can use a rowid/rownum column or pseudo-column to match records side-by-side, such as:
id desc id name
-- ---- --- ----
1 Sprocket 101 Bob
2 Flange 101 Bob
The code would be something like:
select a.id, a.desc, b.id, b.name
from parts a, custs b
where a.rownum = b.rownum;
It's still like a cartesian product but the where clause limits how the rows are combined to form the results (so not a cartesian product at all, really).
I haven't tested that SQL for this since it's one of the limitations of my DBMS of choice, and rightly so, I don't believe it's ever needed in a properly thought-out schema. Since SQL doesn't guarantee the order in which it produces data, the matching can change every time you do the query unless you have a specific relationship or order by clause.
I think the ideal thing to do would be to add a column to both tables specifying what the relationship is. If there's no real relationship, then you probably have no business in trying to put them side-by-side with SQL.
As #Sinto suggested the answer for union and dummy column names following is the whole correct query:
(SELECT id as cId, title, parent_id,level, NULL as indId, NULL as indicator , NULL as cat_id, NULL as subcat_id, NULL as section_id FROM `jm_categories` WHERE ( title LIKE '%births%' )) UNION ALL (SELECT NULL as cId, NULL as title, NULL as parent_id,NULL as level, id as indId, indicator , cat_id, subcat_id, section_id FROM `jm_indicator_setup` WHERE ( indicator LIKE '%births%' ))
We have to match the column names from both tables so that we get records as a combination.

PHP - how to create some column/field depending on another row table

there's some way to create column depending by another row?
I mean like this :
example:
I have table A:
|----|----------|
| id | criteria |
|----|----------|
| 1 | column A |
| 2 | column B |
| 3 | column C |
| .. | ... |//column D, E, F, etc
|----|----------|
how to create a Table B like this?
|column A |column B |column C | ... |
|---------|---------|---------|-----|
| value | value | value |(etc)|
| ... | ... | ... | ... |
|---------|---------|---------|-----|
is it possible with PHP??
I don't know a keyword of this case, so if someone wanna give me a link, it would be appreciate
You can get Criteria from Table A by SELECt query, Assume that you know the SELECT Query, and after that based on result You can run loop for each Criteria get from table A. and for add column Use below query.
IF NOT EXISTS( SELECT NULL
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'tablename'
AND table_schema = 'db_name'
AND column_name = 'columnname') THEN
ALTER TABLE `TableName` ADD `ColumnName` varchar(255) ;
END IF;

Select column values as column headers in SQL?

I have a table like this:
------------------------------------------------------
ID | Date | ClientName | TransactionAmount |
------------------------------------------------------
1 | 6/16/13 | C1 | 15 |
------------------------------------------------------
2 | 6/16/13 | C1 | 10 |
------------------------------------------------------
3 | 6/16/13 | C2 | 10 |
------------------------------------------------------
4 | 6/17/13 | C2 | 20 |
------------------------------------------------------
And I would like to get something like this:
------------------------------------------------------------------------
Date | C1_Total_Amount_Transacted | C2_Total_Amount_Transacted |
------------------------------------------------------------------------
6/16/13 | 25 | 10 |
------------------------------------------------------------------------
6/17/13 | 0 | 20 |
In the second table Date is unique also I there are x clients in the databse the
resul table will have x + 1 columns (1 fore date and x one for each client).
There might be necessary to write some PHP code and more querys, any working solution
is perfect, I don`t need a full SQL solution.
Thanks
I presume that you are rather new to SQL. This type of query requires conditional summation. And it is quite easy to express in SQL:
select `date`,
sum(case when Client_Name = 'C1' then TransactionAmount else 0 end) as C1,
sum(case when Client_Name = 'C2' then TransactionAmount else 0 end) as C2
from t
group by `date`
But, you have to list each client in the query. You always have to specify the exact column headers for a SQL query. If you don't know them, then you need to create the SQL as a string and then execute it separately. This is a rather cumbersome process.
You can often get around that by using group_concat(). This puts the values in a single column, with a separator of your choice (default is a comma):
select `date`, group_concat(amount)
from (select `date`, ClientName, sum(TransactionAmount) as amount
from t
group by `date`, ClientName
) t
group by `date`

i want to retrieve unique values from my table

In a table i have some of column have duplicate values i want to retrieve unique values from my table i used SELECT DISTINCT column_name FROM table_name query and i got unique columns but my problem is i also want id of anyone of the duplicate value how can i retrieve that from using a single query ?
Eg
+----+------+------+
| id | name | po |
+----+------+------+
| 1 | some | 2 |
| 2 | xyzs | 3 |
| 3 | frth | 2 |
| 4 | lopd | 3 |
| 5 | gtry | 2 |
+----+------+------+
i want to find unique po and any one of its id
Output
some thing like this
po - 2 id - ( any of 1,3,5)
po - 3 id - ( any of 2 or 4)
Just group them and get the max id or the min.
SELECT max(id), po FROM table_name group by po
try this:
SELECT MIN(id) id, po
FROM table_name
GROUPB BY po, id
Don't quote me on this, but you might be able to do something like:
SELECT GROUP_CONCAT(id) FROM table_name GROUP BY po
If you don't care which id you will get, then:
SELECT po,id FROM table GROUP BY po
If you wish to get first/last of the ids with that same po, you can add MIN(id)/MAX(id) as well:
SELECT po,MIN(id) as id FROM table GROUP BY po
You can also have all the ids for that po:
SELECT po,GROUP_CONCAT(id) as ids FROM table GROUP BY po

Categories