MySQL query with fallbacks (COALESCE) and JOINS - php

So I am having a hard time trying to write a single query for this. I have a table we will call table_a. I need to pull a single record that matches certain criteria. If the first one produces nothing then fall back to another one. I have been at this awhile and it very well could be something simple I am just missing.
SELECT COALESCE(ta.id,tb.id,0) AS theid,
FROM table_a
RIGHT JOIN (
SELECT table_a.id
FROM table_a
WHERE table_a.field_3='1' AND table_a.field_4='100' AND table_a.user_id='someidhere'
) AS ta
ON table_a.id = ta.id
RIGHT JOIN (
SELECT table_a.id
FROM table_a
WHERE table_a.field_5='2' AND table_a.field_4='100' AND table_a.user_id='someidhere'
) AS tb
ON table_a.id = tb.id
The problem with RIGHT JOIN is if the first one does not produce a record it will not go on to the next one, if I do LEFT JOIN it will pull all the records from table_a, if I specify WHERE table_a.user_id='someidhere' it will obviously pull all the records for that user. I am only looking for one. Essentially only field_3 and field_5 will be the ones that change. If I can not find something for field_3 it moves onto field_5. This will run on a table with 80k records. Thoughts?

Although a newbie, starting out with a cryptic example is not the greatest, but I'm trying here. Your query in itself is rather pointless on what it is doing. Its joining to itself to ultimately grab the ID of a thing that is for a certain user ID and field 4 value, but the field 3 or 5 should be a 1 or 2 respectively.
So, I've written the query directly to the "a" table for the user ID and field 4 which were common regardless of your right-join... THEN added an ( OR ) for your field 3 and 5 columns. This way, I only care about those of the 3 OR 5 value with same user/field 4 area. The ORDER BY clause will force that any record with the field 3 value of 1 is sorted first, then any that are field 5 = 2 will be secondary... So the final first record would be the '1' entry if one existed.
SELECT
table_a.id
FROM
table_a
where
table_a.user_id = 'someidhere'
AND table_a.field_4 = '100'
AND ( table_a.field_3 = '1'
OR table_a.field_5 = '2' )
order by
case when table_a.field_3 = '1'
then 1 else 2 end
So, if there are multiple records returned, you only need to care about the first one returned (in case there are two that qualify).

Related

With SQL (mysql) Append the results of one sub-query to that of another

I have two mysql tables. And essentially two queries (one to each table) that I want to combine within a single SQL Query. Seems like it should be easy (kind of like an inner Join) But inner join is DUPLICATING non-uniqe values from table 2 into my results array... I dont want them duplicated... I want those duplicates from table/query 2 combined into the result record of query1
Query 1 Gets records from table 1. Results are unique. (one ID returns one record) It's simply returning all fields on records where an ID is equal to one of my conditions. [Im using IN instead of a bunch of OR's)
$sid = "'M-179','M-242','M-231','Q-2thru5'" ;
$query = "SELECT * FROM table1 WHERE IN ($sid)`
Query 2 gets records from table 2. But results are NOT unique. One ID can return many records.
$query2 = "SELECT extra_data, pub_url FROM table2 WHERE IN ($sid)";
So I want EACH extra_data & pub_url field from ALL returned records just slapped onto the end of the query 1 result. Am I making sense? So the result would look something like this...
[0] => Array
(
* all my returned fields from the
record returned by query 1 here
$row['extra_data']
$row['pub_url'] <-returned record from query 2
$row['extra_data']
$row['pub_url'] <-another returned record from query 2
$row['extra_data']
$row['pub_url'] <-any another returned
record from query 2, etc..
)
What do you mean by combining the result?
Do you want the results from query2 to appear in the same
relevant rows but just extra columns, or you want them to
appear as new rows.
For the first case, you will have to use JOIN.
But I have a feeling that what you want to do is the second
case. In this case, you will have to use UNION. Note, however,
that in this case the columns of the two queries must match. So
the union of two queries would look like this:
-- pseudo code only
SELECT extra_data, pub_url FROM table1 WHERE IN ($sid)
union
SELECT extra_data, pub_url FROM table2 WHERE IN ($sid)
Try something similar to this, which would join the results of table 1 to table 2 on the column containing the sid value.
SELECT a.*, b.extra_data, pub_url
FROM table1 a
left outer join table2 b on a.sid = b.sid
WHERE a.sid IN ($sid)

Having max of column repeat for each row in mysql

Lets say I have a table A ,
A
user count
a 4
b 1
c 3
d 2
I want to write a query which returns each value of A along with max value of count,something like
user maxCount
a 4
b 4
c 4
d 4
The way I am trying to achieve this is,
select user,max(count) as maxCount from A
The result which I get is :
user maxCount
a 4
Which is clearly not what I want.
I understand I could write multiple queries(i.e. 'select user from A' /'select max(count) from A') as this may seem redundant ,
but I think having it this way would be best for me as I don't want to call mysql multiple times and also I push the returned data as a single jsonObj to the front end , so it is easier to have a single table then combing two returned-tables into one and than converting into json
This query will give you the max value on all rows:
SELECT user,
(SELECT MAX(count) FROM yourTable) AS maxCount
FROM yourTable;
Try this:
select user,max(count) as maxCount from yourTable group by user
That will get your your max counts, and group them by whatever unique values are in the user column.

Simplifing a mysql query

Scenario
I have three elements in this problem. One is an array of ids in this format: (1,3,5,6,8). That array is a list of id of users I want to display. The second element is table that contains user information something simple like: id name surname email. The third element is a second table that contains users configuration. There are two parameters in that last table, one is lid, and the other is usraction (among others, but the important are those two). lid represent a permission type and usraction the value, if the user wants his data to be public there will be a row on that table where lid=3 and usraction="accepted", also I register the datetime of the action every time the user changes this permission, so each time he change it a new row is added, and in order to retrieve the actual state of the permission i have to retrieve the last row for the user an check the value of usraction like this:
SELECT * FROM legallog WHERE uid = '.$user['id'].' AND lid = 3 ORDER BY ABS(`registration` - NOW()) LIMIT 1
Then in php:
if($queryresult && $queryresult[0]['usraction']=='accepted') //display data
The problem
In the scenario id described how im getting the actual state of the permission set by one user at the time, the problem now is I want to sort of clean an array of ids in one or two sql calls. Lets say I want to print the user information of 4 users, one function gives me the ids in this format: (2,6,8,1), but those users may not want to display their information, using the query I showed before I can make a call to the sql server for each user and then generate a new array, if the users who authorize are 1 and 8 the result array will be (8,1).
The think is whit an array of 100 users I will make 100 calls, and I dont want this, is there a way to solve this in one or two querys?
A query such as this gets you the information you want:
select u.*,
(select usraction from configuration c where c.userid = u.userid and c.lid = 3 order by datetime limit 1
) as lastLid3Action
from users u
where u.userid in (1,3,5,6,8)
If you only want "accepted" values, then make this a subquery:
select t.*
from (select u.*,
(select usraction from configuration c where c.userid = u.userid and c.lid = 3 order by datetime limit 1
) as lastLid3Action
from users u
where u.userid in (1,3,5,6,8)
) t
where LastLid3Action = 'Accepted'
As I understand you have two tables in the first table, where the user information is stored; and the second table, where the user permission is stored. And you want to get information from the first table using permission from the second table, then you need this query:
SELECT a.*
FROM first-table-name AS a
RIGHT JOIN second-table-name AS b a.uid = b.lid
WHERE uid in (1,3,5,6,8) AND usraction='accepted'
ORDER BY ABS)
LIMIT 1
You question is somewhat vague, but if you are asking how to select a number of records when you have a list of ids, the answer is:
select column, list, goes, here from tablename
where id in (1,5,8,12,413);
That will get you the values of the columns you list for just the records that match your array of ids.

How to find duplicates in database?

There are many questions on how to find duplicates in a database, but not with the specific problem that I have.
I have a table with approx. 120000 entries. I need to find duplicates. To find them, I use a php script that is structured like the following:
//get all entries from database
//loop through them
//get entries with greater id
//compare all of them with the original one
//update database (delete duplicate, update information in linked tables, etc.)
It is not possible to sort out all duplicates already in the initial query, because I have to loop through all entries since my duplicate search is sensitive not only to entries that are 100% alike, but also entries that are 90% alike. I use similar_text() for that.
I think the first loop is okay, but looping through all other entries within the loop is just too much. With 120000 entries this would be close to (120000^2)/2 iterations.
So instead of using a loop within the loop, there must be a better way to do it. Do you have any ideas? I thought about using in_array(), but it is not sensitive to something like 90% string similarity, and also doesn't give me the array's fields it found the duplicates in - I would need those to get the entries' ids to update the database correctly.
Any ideas?
Thank you very much!
Charles
UPDATE 1
The query I am using right now is the following:
SELECT a.host_id
FROM host_webs a
JOIN host_webs b ON a.host_id != b.host_id AND a.web = b.web
GROUP BY a.host_id
It shows originals and duplicates perfectly, but I need to get rid of the originals, i.e. the first ones found with the associated data. How can I accomplish that?
You can JOIN the table onto itself and do it all in SQL (I know you say you don't think you can, but I would be surprised if this is the case). All you need to do is put all the columns you use to test for duplicates into the ON clause of the JOIN.
SELECT id
FROM tablename a
JOIN tablename b ON a.id != b.id AND a.col1 = b.col1 AND a.col2 = b.col2
GROUP BY id
This will return just the ids of the rows where col1 and col2 are duplicated. You can incorporate whatever string comparisons you need into this, the ON clause can be as complicated as you need it to be. For example:
SELECT id
FROM tablename a
JOIN tablename b ON a.id != b.id AND
(a.col1 = b.col1 AND (a.col2 = b.col2 OR a.col3 = b.col3))
OR ((a.col1 = b.col1 OR a.col2 = b.col2) AND a.col3 = b.col3)
OR (SOUNDEX(a.col1) = SOUNDEX(b.col1) AND SOUNDEX(a.col2) = SOUNDEX(b.col2) AND SOUNDEX(a.col3) = SOUNDEX(b.col3))
GROUP BY id
EDIT
Since all you are actually doing with your query is looking for rows where the web column is identical, this would do the job of finding only the duplicates and not the original "good" records - assuming host_id is numeric and that the "good" record would be the one with the lowest host_id:
SELECT b.host_id
FROM host_webs a
INNER JOIN host_webs b ON b.web = a.web AND b.host_id > a.host_id
GROUP BY b.host_id
I imagine the end game here would be to remove the duplicates, so if you are feeling brave you could actually delete them in one go:
DELETE b.*
FROM host_webs a
INNER JOIN host_webs b ON b.web = a.web AND b.host_id > a.host_id
The GROUP BY is not necessary in the DELETE statement because it doesn't matter if you try and delete the same row more than once in a single statement.
If you're doing a 1-time removal of duplicate items, I wouldn't bother writing a php script - it's cleaner to do it in sql.
The general algorithm for removing duplicates that I find works the best is:
1. duplicate the table
2. truncate the original table
3. set a unique index on whichever columns need to be unique
4. reinsert the rows using either INSERT IGNORE INTO original_table SELECT * FROM duplicate_table OR REPLACE INTO original_table SELECT * FROM duplicate table
5. fixed linked tables - remove orphaned rows (DELETE x FROM x LEFT JOIN original TABLE ON (...) WHERE original_table.id IS NULL)

reading data via PDO - the same row is brought twice

I read rows from some mssql table via PHPs PDO.
Some rows, are brought twice, exactly same rows, with exactly the same id values
This happens to specific rows. Each time I run my import script, the issue happens on the very same rows. For example, after bringing some 16,000 rows correctly, one row, the same one each time, is brought twice.
The duplication occurs in a row. The line is brought, and the next fetch() request returns the very same row.
When I run:
select * from MY_TABLE where id='the problematic id'
only one row is returned, not two
Any ideas what (the hell) can go on here?
Thank you very much guys
edit:
The query that is being run:
select o.accountid, c.contactid, o.opportunityid, o.createdate, o.modifydate, o.createuser, o.modifyuser, o.description, o.projclosedate, o.notes, o.accountmanagerid
from sysdba.opportunity o
left join sysdba.opportunity_contact oc on o.opportunityid = oc.opportunityid and oc.salesrole = 'speaker' ";
left join sysdba.contact c on c.contactid = oc.contactid
where o.status <> 'Inactive'
order by o.opportunityid asc;
I think you need to join your contact table to your opportunity table. It seems that you might not have a 1 to 1 mapping between those tables the way you have it set up. See below:
--This should reference the "o" table but it doesn't.
left join sysdba.contact c on c.contactid = oc.contactid
If that's not the case then you should really be joining around the opportunity_contact table instead (put it as your 'from' table).

Categories