Returning values even the result are empty - php

I have a table that is similar below.
| user_id | point_1 | point_2 | point_3
453123 1234 32 433
321543 1 213 321
My query is something like this:
$query = "SELECT * FROM my_table WHERE user_id = 12345 OR user_id = 987654321"
Obviously, this will return nothing since user_id 12345 OR user_id 987654321 do not exist on the table.
But I still want to return something like the one below :
| user_id | point_1 | point_2 | point_3
12345 0 0 0
987654321 0 0 0

You could use an inline view as a rowsource for your query. To return a zero in place of a NULL (which would be returned by the outer join when no matching row is found in my_table, you can use the IFNULL function.
e.g.
SELECT s.user_id
, IFNULL(t.point_1,0) AS point_1
, IFNULL(t.point_2,0) AS point_2
, IFNULL(t.point_3,0) AS point_3
FROM ( SELECT 12345 AS user_id
UNION ALL SELECT 987654321
) s
LEFT
JOIN my_table t
ON t.user_id = s.user_id
NOTE: If datatype of user_id column my_table is character, then I'd enclose the literals in the inline view in single quotes. e.g. SELECT '12345' AS user_id. If the characterset of the column doesn't match your client characterset, e.g. database column is latin1, and client characterset is UTF8, you'd want to force the character strings to be a compatible (coercible) characterset... SELECT _latin1'12345' AS user_id

You can't get the result you want using only a select statement. Only rows that exist somewhere will be returned.
The only way I can think to do this is to insert the query values into a temp table and then outer join against that for your query.
So the basic process would be:
create table temp1 (user_id integer);
insert into temp1 values (987654321); -- repeat as needed for query.
select t.user_id, m.* from temp1 t left outer join my_table m on m.user_id = t.user_id;
drop table temp1;
This isn't very efficient though.

Your desired result resembles the result of an OUTER JOIN - when some records exist only in one table and not the other, an OUTER JOIN will show all of the rows from one of the joined tables, filling in missing fields from the other table with NULL values.
To solve your particular problem purely in SQL, you could create a second table that contains a single field with all of the user_id values that you want to be able to show in your result. Something like:
+-----------+
| user_id |
+-----------+
| 1 |
+-----------+
| 2 |
+-----------+
| 3 |
+-----------+
| ... |
+-----------+
| 12344 |
+-----------+
| 12345 |
+-----------+
| 12346 |
+-----------+
| ... |
+-----------+
And so on. If this second table is named all_ids, you could then get your desired result by modifying your query as follows (exact syntax may vary by database implementation):
SELECT
*
FROM
all_ids AS i
LEFT OUTER JOIN
my_table AS t ON i.user_id = t.user_id
WHERE
i.user_id = 12345
OR i.user_id = 987654321;
This should produce the following result set:
+-----------+----------+----------+----------+
| user_id | point_1 | point_2 | point_3 |
+-----------+----------+----------+----------+
| 12345 | NULL | NULL | NULL |
+-----------+----------+----------+----------+
| 987654321 | NULL | NULL | NULL |
+-----------+----------+----------+----------+
It should be noted that this table full of IDs could take up a significant amount of disk space. An integer column in MySQL can hold 4,294,967,296 4-byte values, or 16 GB of data sitting around purely for your convenience in displaying some other data you don't have. So unless you need some smaller range or set of IDs available, or have disk space coming out your ears, this approach simply may not be practical.
Personally, I would not ask the database to do this in the first place. Essentially it's a display issue; you already get all the information you need from the fact that certain rows were not returned by your query. I would solve the display issue outside of the database, which in your case means filling in those zeroes with PHP.

Related

PHP how to use where in to replace left join in mysql?

I have 2 table, records and common_member.
records just store a plugin data. (Not frequently queried) but common_member was a huge data table and frequently queried.
Sample data of common_member
+--------+-----------+
| uid | username |
+--------+-----------+
| 1 | admin |
| 2 | test1 |
+--------------------+
Sample data of records
+--------+-----------+-----------+
| uid | amount |createtime |
+--------+-----------+-----------+
| 1 | 50 |1234567 |
| 1 | 100 |5555555 |
| 2 | 2000 |9999999 |
+--------------------------------+
Normally I am using $lrecord = mysqli_fetch_all("SELECT t1.*,t2.username FROM records t1 LEFT JOIN common_member t2 ON (t1.uid = t2.uid) ORDER BY t1.createtime").
But after that I was be informed if using leftjoin connect with common_member, It will be very inefficient and there was a way just using where in the get the username from common_member.
So, how to get the username from common_member without using leftjoin?
I want the final result of records array to be: (Means username from common_member join to records)
+------+-------------+-----------+-----------+
| uid | username | amount | createtime|
+------+-------------+-----------+-----------+
| 1 | admin | 50 | 1234567 |
| 1 | admin | 100 | 5555555 |
| 2 | test1 | 2000 | 9999999 |
+--------------------------------------------+
My question is, how to use where in get the username in common_member without leftjoin ?
Thank you.
But after that I was be informed...
(with respect...) who informed you ? phpMyAdmin ? another interface ? your friend ? a blog.. ?
there is no problem with the left join, but you must check the indexes of your tables.
It is necessary that in both tables, there is an index (preferably primary) on the field uid.
Otherwise, the request will not be optimized and can be slow.
The answer to your question, how to use where in get the username in common_member without leftjoin ?, is that you can't.
The where clause is used for filtering results, not to add information to them. This could be usefull if you just want to retrive all rows in records which have an existing user. However, if database was properly designed, such a question wouldn't be asked, because records.uid would be an non-nullable foreign key referencing common_member.uid.
Hence, it wouldn't be necessary to make a left join, but only a join.
But, supposing this isn't your case, and the database isn't properly designed, and records.uid isn't a foreign key, then
select *
from records
where uid in (
select uid
from common_member
)
would return all records of existing users. But still, no username.
The only solution is to execute a join, as you are already doing:
SELECT t1.*,t2.username
FROM records t1
JOIN common_member t2 ON (t1.uid = t2.uid)
ORDER BY t1.createtime

Find unmatched results obtained by two different queries on two different tables [duplicate]

I've got the following two tables (in MySQL):
Phone_book
+----+------+--------------+
| id | name | phone_number |
+----+------+--------------+
| 1 | John | 111111111111 |
+----+------+--------------+
| 2 | Jane | 222222222222 |
+----+------+--------------+
Call
+----+------+--------------+
| id | date | phone_number |
+----+------+--------------+
| 1 | 0945 | 111111111111 |
+----+------+--------------+
| 2 | 0950 | 222222222222 |
+----+------+--------------+
| 3 | 1045 | 333333333333 |
+----+------+--------------+
How do I find out which calls were made by people whose phone_number is not in the Phone_book? The desired output would be:
Call
+----+------+--------------+
| id | date | phone_number |
+----+------+--------------+
| 3 | 1045 | 333333333333 |
+----+------+--------------+
There's several different ways of doing this, with varying efficiency, depending on how good your query optimiser is, and the relative size of your two tables:
This is the shortest statement, and may be quickest if your phone book is very short:
SELECT *
FROM Call
WHERE phone_number NOT IN (SELECT phone_number FROM Phone_book)
alternatively (thanks to Alterlife)
SELECT *
FROM Call
WHERE NOT EXISTS
(SELECT *
FROM Phone_book
WHERE Phone_book.phone_number = Call.phone_number)
or (thanks to WOPR)
SELECT *
FROM Call
LEFT OUTER JOIN Phone_Book
ON (Call.phone_number = Phone_book.phone_number)
WHERE Phone_book.phone_number IS NULL
(ignoring that, as others have said, it's normally best to select just the columns you want, not '*')
SELECT Call.ID, Call.date, Call.phone_number
FROM Call
LEFT OUTER JOIN Phone_Book
ON (Call.phone_number=Phone_book.phone_number)
WHERE Phone_book.phone_number IS NULL
Should remove the subquery, allowing the query optimiser to work its magic.
Also, avoid "SELECT *" because it can break your code if someone alters the underlying tables or views (and it's inefficient).
The code below would be a bit more efficient than the answers presented above when dealing with larger datasets.
SELECT *
FROM Call
WHERE NOT EXISTS (
SELECT 'x'
FROM Phone_book
WHERE Phone_book.phone_number = Call.phone_number
);
SELECT DISTINCT Call.id
FROM Call
LEFT OUTER JOIN Phone_book USING (id)
WHERE Phone_book.id IS NULL
This will return the extra id-s that are missing in your Phone_book table.
I think
SELECT CALL.* FROM CALL LEFT JOIN Phone_book ON
CALL.id = Phone_book.id WHERE Phone_book.name IS NULL
SELECT t1.ColumnID,
CASE
WHEN NOT EXISTS( SELECT t2.FieldText
FROM Table t2
WHERE t2.ColumnID = t1.ColumnID)
THEN t1.FieldText
ELSE t2.FieldText
END FieldText
FROM Table1 t1, Table2 t2
SELECT name, phone_number FROM Call a
WHERE a.phone_number NOT IN (SELECT b.phone_number FROM Phone_book b)
Alternatively,
select id from call
minus
select id from phone_number
Don't forget to check your indexes!
If your tables are quite large you'll need to make sure the phone book has an index on the phone_number field. With large tables the database will most likely choose to scan both tables.
SELECT *
FROM Call
WHERE NOT EXISTS
(SELECT *
FROM Phone_book
WHERE Phone_book.phone_number = Call.phone_number)
You should create indexes both Phone_Book and Call containing the phone_number. If performance is becoming an issue try an lean index like this, with only the phone number:
The fewer fields the better since it will have to load it entirely. You'll need an index for both tables.
ALTER TABLE [dbo].Phone_Book ADD CONSTRAINT [IX_Unique_PhoneNumber] UNIQUE NONCLUSTERED
(
Phone_Number
)
WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ONLINE = ON) ON [PRIMARY]
GO
If you look at the query plan it will look something like this and you can confirm your new index is actually being used. Note this is for SQL Server but should be similar for MySQL.
With the query I showed there's literally no other way for the database to produce a result other than scanning every record in both tables.

When joining tables, date column is NULL on some etries

I am trying to migrate some custom CMS DB to Wordpress, and so far it's been a living hell.
I am using WP All import plugin, so I need a neat single .csv export that contains data from multiple tables from this custom cms database.
So, these are the columns from two tables that I want to join:
`eo_items`
| cat_id | identificator | create_date |
---------------------------------------------
| 1 | Title of the post | 1283786285 |
`eo_items_trans`
| item_id | lid | name | s_desc | l_desc |
---------------------------------------------------------
| 1 | 33 | Title of the post | excerpt | content |
Desired result should be:
| item_id | lid | name | s_desc | l_desc | cat_id | create_date |
--------------------------------------------------------------------------------
| 1 | 33 | Title of the post | excerpt | content | 1 | Some date |
Here is the script I am using:
SELECT DISTINCT
eo_items_trans.item_id,
eo_items_trans.lid,
eo_items.cat_id,
DATE_FORMAT( eo_items.create_date, '%d.%m.%Y' ) create_date,
eo_items_trans.s_desc,
eo_items_trans.l_desc,
eo_items_trans.name
FROM eo_items_trans
LEFT JOIN eo_items ON ( eo_items_trans.name = eo_items.identificator )
Trouble with this code is that in resulting table some date columns are NULL, and I don't know if the result is what I need because the table has around 2000 rows and I don't know how to cross check if category IDs are correctly populated.
This is the first time I am doing something like this with MySQL so I am really not sure if the procedure is right for what I am trying to achieve.
If you need any clarifications please ask.
EDIT:
eo_items table has some 300 rows more than eo_items_trans so there are some records there that don't have corresponding records in eo_items_trans. I am guessing this should be reflected in the query as well?
Since you're using a LEFT JOIN, NULLs will be returned for any rows of eo_items_trans that do not have entries in oe_items. This could mean the eo_items.identificator is empty, or doesn't exactly match the name (case sensitivity will apply).
You'll have to investigate and clean up the data for rows in eo_items_trans missing the expected row in eo_items.
You NULL results for date seem to come or from eo_items_trans records that have no corresponding entry in the eo_items table or from eo_items records where create_date is null.
You can easily crosscheck check by doing the following
Is there records in eo_items_trans that have no corresponding entries in eo_items:
SELECT DISTINCT eo_items_trans.name FROM eo_items_trans
where NOT EXISTS (
SELECT * FROM eo_items
where eo_items.identificator = eo_items_trans.name
)
If this yields one ore more rows, that will be the eo_items_trans.name records with no correspondent in eo_items. If this is you problem, the do a JOIN, not a LEFT join in your main query
As for empty dates in eo_items you might want to check like this
SELECT * from eo_items WHERE create_date IS NULL
If you find records here, this is where yout NULL values in the main query come from

WHERE vs HAVING in generated queries

I know that this title is overused, but it seems that my kind of question is not answered yet.
So, the problem is like this:
I have a table structure made of four tables (tables, rows, cols, values) that I use to recreate the behavior of the information_schema (in a way).
In php I am generating queries to retrieve the data, and the result would still look like a normal table:
SELECT
(SELECT value FROM `values` WHERE `col` = "3" and row = rows.id) as "col1",
(SELECT value FROM `values` WHERE `col` = "4" and row = rows.id) as "col2"
FROM rows WHERE `table` = (SELECT id FROM tables WHERE name = 'table1')
HAVING (col2 LIKE "%4%")
OR
SELECT * FROM
(SELECT
(SELECT value FROM `values` WHERE `col` = "3" and row = rows.id) as "col1",
(SELECT value FROM `values` WHERE `col` = "4" and row = rows.id) as "col2"
FROM rows WHERE `table` = (SELECT id FROM tables WHERE name = 'table1')) d
WHERE col2 LIKE "%4%"
note that the part where I define the columns of the result is generated by a php script. It is less important why I am doing this, but I want to extend this algorithm that generates the queries for a broader use.
And we got to the core problem, I have to decide if I will generate a where or a having part for the query, and I know when to use them both, the problem is my algorithm doesn't and I have to make a few extra checks for this. But the two above queries are equivalent, I can always put any query in a sub-query, give it an alias, and use where on the new derived table. But I wonder if I will have problems with the performance or not, or if this will turn back on me in an unexpected way.
I know how they both work, and how where is supposed to be faster, but this is why I came here to ask. Hopefully I made myself understood, please excuse my english and the long useless turns of phrases, and all.
EDIT 1
I already know the difference between the two, and all that implies, my only dilemma is that using custom columns from other tables, with variable numbers and size, and trying to achieve the same result as using a normally created table implies that I must use HAVING for filtering the derived tables columns, at the same time having the option to wrap it up in a subquery and use where normally, this probably will create a temporary table that will be filtered afterwards. Will this affect performance for a large database? And unfortunately I cannot test this right now, as I do not afford to fill the database with over 1 billion entries (that will be something like this: 1 billion in rows table, 5 billions in values table, as every row have 5 columns, 5 rows in cols table and 1 row in tables table = 6,000,006 entries in total)
right now my database looks like this:
+----+--------+-----------+------+
| id | name | title | dets |
+----+--------+-----------+------+
| 1 | table1 | Table One | |
+----+--------+-----------+------+
+----+-------+------+
| id | table | name |
+----+-------+------+
| 3 | 1 | col1 |
| 4 | 1 | col2 |
+----+-------+------+
where `table` is a foreign key from table `tables`
+----+-------+-------+
| id | table | extra |
+----+-------+-------+
| 1 | 1 | |
| 2 | 1 | |
+----+-------+-------+
where `table` is a foreign key from table `tables`
+----+-----+-----+----------+
| id | row | col | value |
+----+-----+-----+----------+
| 1 | 1 | 3 | 13 |
| 2 | 1 | 4 | 14 |
| 6 | 2 | 4 | 24 |
| 9 | 2 | 3 | asdfghjk |
+----+-----+-----+----------+
where `row` is a foreign key from table `rows`
where `col` is a foreign key from table `cols`
EDIT 2
The conditions are there just for demonstration purposes!
EDIT 3
For only two rows, it seems there is a difference between the two, the one using having is 0,0008 and the one using where is 0.0014-0.0019. I wonder if this will affect performance for large numbers of rows and columns
EDIT 4
The result of the two queries is identical, and that is:
+----------+------+
| col1 | col2 |
+----------+------+
| 13 | 14 |
| asdfghjk | 24 |
+----------+------+
HAVING is specifically for GROUP BY, WHERE is to provide conditional parameters. See also WHERE vs HAVING
I believe the having clause would be faster in this case, as you're defining specific values, as opposed to reading through the values and looking for a match.
See: http://database-programmer.blogspot.com/2008/04/group-by-having-sum-avg-and-count.html
Basically, WHERE filters out columns before passing them to an aggregate function, but HAVING filters the aggregate function's results.
you could do it like that
WHERE col2 In (14,24)
your code WHERE col2 LIKE "%4%" is bad idea so what about col2 = 34 it will be also selected.

delete duplicate rows that have blob text / mediumtext mysql

I have seen lots of posts on deleting rows using sql commands but i need to filter out rows which have mediumtext.
I keep getting an error Error Code: 1170. BLOB/TEXT column used in key specification without a key length from solution such as:
ALTER IGNORE TABLE foobar ADD UNIQUE (title, SID)
My table is simple, i need to check for duplicates in mytext, id is unique and they are AUTO_INCREMENT.
As a note, the table has about a million rows, and all attempts keep timing out. I would need a solution that performs actions in batches such as WHERE id>0 AND id<100
Also I am using MySQL Workbench on amazons RDS
From a table like this
+---+-----+-----+------+-------+
|id |fname|lname|mytext|morevar|
|---|-----|-----|------|-------|
| 1 | joe | min | abc | 123 |
| 2 | joe | min | abc | 123 |
| 3 | mar | kam | def | 789 |
| 4 | kel | smi | ghi | 456 |
+------------------------------+
I would like to end up with a table like this
+---+-----+-----+------+-------+
|id |fname|lname|mytext|morevar|
|---|-----|-----|------|-------|
| 1 | joe | min | abc | 123 |
| 3 | mar | kam | def | 789 |
| 4 | kel | smi | ghi | 456 |
+------------------------------+
update forgot to mention this is on amazon RDS using mysql workbench
my table is very large and i keep getting an error Error Code: 1205. Lock wait timeout exceeded from this sql command:
DELETE n1 FROM names n1, names n2 WHERE n1.id > n2.id AND n1.name = n2.name
Also, if anyone else is having issues with MySQL workbench timing out the fix is
Go to Preferences -> SQL Editor and set to a bigger value this parameter:
DBMS connection read time out (in seconds)
OPTION #1: Delete all duplicates records leaving one of each (e.g. the one with max(id))
DELETE
FROM yourTable
WHERE id NOT IN
(
SELECT MAX(id)
FROM yourTable
GROUP BY mytext
)
You could prefer using min(id).
Depending on the engine used, this won't work and, as it did, give you the Error Code: 1093. You can't specify target table 'yourTable' for update in FROM clause. Why? Because deleting one record may cause something to happen which made the WHERE condition FALSE, i.e. max(id) changes the value.
In this case, you could try using another subquery as a temporary table:
DELETE
FROM yourTable
WHERE id NOT IN
(
SELECT MAXID FROM
(
SELECT MAX(id) as MAXID
FROM yourTable
GROUP BY mytext
) as temp_table
)
OPTION #2: Use a temporary table like in this example or:
First, create a temp table with the max ids:
SELECT MAX(id) AS MAXID
INTO tmpTable
FROM yourTable
GROUP BY mytext;
Then execute the delete:
DELETE
FROM yourTable
WHERE id NOT IN
(
SELECT MAXID FROM tmpTable
);
How about this it will delete all the duplicate records from the table
DELETE t1 FROM foobar t1 , foobar t2 WHERE t1 .mytext= t2.mytext

Categories