Return only one row from duplicate rows - php

I am accessing a database with names tied to companies. Names are unique in the table. There can be multiple people that have the same company. I need to return only ONCE instance of the company if is duplicate, so that I can display the information according to company. My code is as follows:
$result = $pdo->query('SELECT * FROM vendoreducation WHERE company LIKE "TestCompany" GROUP BY company');
That obviously doesnt work, and I dont want to specify individual companies. I just want to be able to return a list of all of the companys in the DB. If there are multiple entries that have the same company name, just return one of those.
EDIT
The answer I chose was in the right direction, but the answer that I ended up going with was m.buettner's answer.

I would ask for clarification in a comment, but I am lacking the rep, sorry. If you only need one row per company, does it even matter what the other columns are? Or do you really just want to find out a list of all companies?
For the latter, this should do:
$result = $pdo->query('SELECT DISTINCT company FROM vendoreducation;');
Of course, you could also include any columns that will be identical for all rows of the same company. However, since some columns will probably have values that differ (like the names you mentioned), using DISTINCT with * will probably just give you all rows again.
EDIT: If you DO want one full row per company you could simply use the list of all companies retrieved by the line above and then query one row for every single company, by using the LIMIT 1 solution with every individual company in turn. But maybe then you should think about normalization anyway if that is an option at all (i.e. if you can alter the database).

Use DISTINCT
$result = $pdo->query('SELECT DISTINCT * FROM vendoreducation WHERE company LIKE "TestCompany"');

$result = $pdo->query('SELECT * FROM vendoreducation WHERE company LIKE "TestCompany" GROUP BY company LIMIT 1');
OR
$result = $pdo->query('SELECT distinct * FROM vendoreducation WHERE company LIKE "TestCompany" GROUP BY company');

Related

Combining 3 tables to generate a list of profiles (HTML/PHP/MySQL)

I'm learning PHP/MySQL to provide info on/to local farmers through a website. Unfortunately, I have reached two seemingly tricky obstacles, which I'm struggling to overcome.
I have 3 tables, 'pro_list'(PRIMARY KEY producer_id), 'product_list'(PRIMARY KEY product_id) and an intermediate 'prod_to_prod'(FOREIGN KEYS producer_id and product_id) to enable a many-to-many relationship between the other two.
The idea is to generate a list of div elements containing info (e.g. phone, email, location...) taken from 'pro_list' and what they produce, from 'product_list', using their bridge with 'prod_to_prod' (as one producer can produce many products, and the same product can be produced by many different producers).
My problems start already with generating the basis query, which should return an elegant list of producers, using the bridge as a reference (i.e. I want the list to display only the producers whose producer_id has product_ids associated to it in 'prod_to_prod').
As you can imagine, the code I used...
<?php
$pagequery="SELECT * FROM pro_list
OUTER JOIN prod_to_prod
WHERE pro_list.producer_id=prod_to_prod.producer_id
LIMIT $offset, $num_of_cells";
$res_pagequery=mysqli_query($conn,$pagequery);
while($proddiv=mysqli_fetch_array($res_pagequery)) {
echo "<div>
...Producer:".$proddiv['prod_firstname']."...
</div>;
}
?>
...has returned a list with numerous duplicates, as many producer_ids are associated with different product_ids in 'prod_to_prod'. I have tried numerous combinations using SELECT DISTINCT but, so far, all I've obtained where error messages.
I don't even know how to approach the extended query (i.e. display the names of the products associated to each producer in the corresponding div, based on the associations pro_list.product_id=prod_to_prod.product_id AND product_list.product_id=prod_to_prod.product_id. My guess is that I would have to work with UNION of at least two SELECTs, but since I'm only obtaining either duplicates or errors, I wouldn't even dare trying...
What would be your approach towards solving this?
When you do an (LEFT) OUTER JOIN you get at least one row for every Product whether or not there is a matching row in prod_to_prod. You want to change that to an INNER JOIN, which is actually the default -- you just use JOIN.
As you've noted, you will get a row in the result set for each match in prod_to_prod.
If you use SELECT * you are going to get all columns, but as you do not care about any of the columns from prod_to_prod, you can instead specify the specific columns you want or you can specify SELECT pro_list.* In doing so, DISTINCT will be useful for eliminating duplicates, or you can use GROUP BY pro_list.producer_id to get one row per producer.

MYSQL Database Design/SELECT Statement assistance

I'm hoping someone may be able to provide some advice regarding a database schema I have created and a SELECT statement that I am using to query the database.
I am attempting to create a database of very old newspaper articles from the 1800's, storing such things as the date, title and full text of the article, an image of the article, the name of the newspaper the article came from, names of locations mentioned in the article and individuals mentioned within the article.
Basically below is the current structure I've created with tbArticle being the main table focus ("test" is the name of the database). I've normalised the name of the newspaper, image info, location info and individuals into their own tables and because it is assumed there will be many articles to many individuals, I've added a link table (lktbArticleIndividuals) of sorts between tbArticle & tbIndividual;
The reason for creating the database is to obviously make a focused set of newspaper articles searchable and store them in a logical format.
My issue or question is this ...
All I want to do is display a list of all the articles in the database, obviously including data from the other tables other than tbArticle and to do this I am using this SELECT query;
SELECT *
FROM tbArticle a
, tbLocation l
, tbNewspapers n
, tbIndividual i
, lktbArticleIndividuals ai
, tbImage m
WHERE a.idLocation = l.idLocation
and a.idNewspaper = n.idNewspaper
and a.idArticle = ai.idArticle
and ai.idIndividual = i.idIndividual
and a.idImage = m.idImage;
Which does what I want ... except ... if more than one individual is listed as being in an article, then two (or more) instances of the whole article are returned with the only difference being the different individual's names being displayed.
If possible, I want to just list each article ONCE, but iterate through the two or more individuals to include them. Can this be done?
If I were to query the database in say PHP I suspect what I might have to do is some sort of loop within a loop to achieve the results I want, but this doesn't seem very efficient to me!!
Does any of this make sense to anyone?!
Instead of SELECT *, you could name the columns you're interested in, and for things such as individuals, use GROUP_CONCAT() to add them all into one field, and at the end of your query, use GROUP BY a.idArticle to limit each article to one row per article.
Assuming you just want the first_name of each individual you could use a group by with a GROUP_CONCAT.
SELECT *,
GROUP_CONCAT(i.firstname)
FROM tbArticle a
, tbLocation l
, tbNewspapers n
, tbIndividual i
, lktbArticleIndividuals ai
, tbImage m
WHERE a.idLocation = l.idLocation
and a.idNewspaper = n.idNewspaper
and a.idArticle = ai.idArticle
and ai.idIndividual = i.idIndividual
and a.idImage = m.idImage;
GROUP BY a.idArticle
However, if you want to get many details of each individual I would encourage you to do two separate queries: one for the articles and another one to get the individuals of each article.

Dynamic MySQL query: Join results from numerous tables

Something I've been stumbling on for a while now. I have a table called gears which contains rows with the names: id, mid, cid and installed. I want to search this table and return in csv format a list of mids for some unique cid. For example if cid = $cid I can use:
$query = $database -> query("SELECT COUNT(mid), GROUP_CONCAT(mid) FROM gears WHERE cid=$cid", __LINE__, __FILE__);
$gears_installed = $database -> get_result($query);
$gears = $database -> get_result($query, 0, 1);
Don't worry about the function names, they do exactly as one would expect. So if there were 3 rows for that specific $cid, with mids: bank, lottery and post then $gears_installed would be equal to 3 and $gears would be equal to bank,lottery,post. This works as intended.
Now on to the question I have. Each unique mid has its own table, named settings_mid_here. I.e, for the above three, I have the tables settings_bank, settings_lottery and finally settings_post. Each of these tables will also have a column called cid (this is how the two can be related). How do I go about running one query to return the entire row from each table where cid=$cid? I do not want to run a separate query for SELECT * FROM settings_bank WHERE cid=$cid and SELECT * FROM settings_post WHERE cid=$cid and finally SELECT * FROM settings_post WHERE cid=$cid, as this could result in around 10 extra queries on one page load (there are, at the moment, 10 different mids).
As you can see, the problem is dynamic. It must be able to adapt to a different number of mids, somehow differentiate the settings within each table (for example settings_bank may have a column with name name, and so might settings_post). Finally, it must also be able to return a default row (not null values) if there does not exist a row corresponding to the given $cid.
A complicated task but I hope someone can help me with this as I have not been able to get anywhere.
$queries = array();
foreach(explode(',', $gears) as $gear) {
$queries[] = "SELECT '$gear' AS gearname, settings_$gear.* FROM settings_$gear WHERE cid=$cid";
}
$sql = implode(' UNION ', $queries);
$query2 = $database->query($sql);
This query will return one row for each table, with an extra gearname column to indicate which table that row came from.
Or you can create a JOIN dynamically:
$gears_array = explode(',', $gears);
$joins = implode(' JOIN ', $gears_array);
$wheres = implode(' AND ',
array_map(function($g) use ($cid) {
return "$g.cid = $cid";
}, $gears_array));
$sql = "SELECT * FROM $joins WHERE $wheres";
$query2 = $database->query($sql);
This is not really an answer to your specific question, simply because there's no way to accomplish what you are trying with one query.
The reason is simple: RDBMSs are not designed to work this way. Tables are supposed to store data that represent entities and relations. In your case, for each distinct value of mid, a table named settings_{mid} must exist, thus forcing the mid column implicitly store (a part of) a table name. But that's not data, that's metadata.
That would not really be a problem if SQL syntax could accept variable, parametrized, column-related or arbitary table names. But it doesn't. And that's by design. Instead, an RDBMS provides you with all the tools you could ever need to relate your data to each other. By using it the intended way, you'll never have to resort to such 'dynamic' tricks.
In your case, there should be one config table with a mid column to distinguish the rows that refer to the specific mid value. Then, the query would be simple:
select * from `config` where mid='$mid' and cid='$cid'
This is the relational way. Thus the R in RDBMS. There's absolutely no reason at all to mix data with metadata. If you do, you move the relation resolution problem in higher levels of the application model.
And one last thing: One might argue that the config_{mid} tables might have similar but not identical structure. There's a solution for that too: IS-A relations.
Having said that, for your specific problem, a solution along the lines of Barmar's answer would do the trick.

postgresql: how to SELECT entries that satisfy a condition

I am trying to select the entries that have the same text in the "email" column of my postgreSQL table. I am completly new to database and this is the first database I ever created, so sorry if it's a silly question. My database has 3 columns: key, user_mails and json_adress.
I tried
$existent= "SELECT * FROM
(SELECT public.account_recover_users.* AS keys,emails,jsons
FROM public.account_recover_users)
WHERE emails='$email'";
but I guess I am mistaking somewhere.
Please help, I am trying to learn and I got a bit stuck.
The reason you got the error ERROR: subquery in FROM must have an alias Hint: For example, FROM (SELECT ...) [AS] foo is because you have to give an alias (nickname) to any subquery you use. So, just do what the error message hint tells you to do:
"SELECT *
FROM (
SELECT public.account_recover_users.* AS keys, emails, jsons
FROM public.account_recover_users
) as subq
WHERE emails='$email'"
But you don't need a subquery at all. This could be simplified to just:
"SELECT * FROM account_recover_users WHERE user_mails='$email'"
If you want to rename (i.e. give an alias to) your columns upon selection, I wouldn't use a subquery. Try:
"SELECT key as keys, user_mails as emails, json_adress as jsons
FROM account_recover_users
WHERE emails='$email'"
I don't really recommend this, though. If you're just going to give an alias to every column, why not rename the columns in the database?
I am writing another answer, because the currently accepted answer is wrong in several respects. Neither of the two presented queries can work, even though the OP incorrectly accepted the answer.
The original query couldn't work for several reasons:
As was mentioned (and the error message clearly states): a subquery requires an alias.
As was also mentioned, a subquery is just pointless for the simple task.
This construct is nonsense: public.account_recover_users.* AS keys You can't apply an alias after expanding * to the list of columns. Postgres just ignores it. It might throw an exception in future releases.
According to your description, existing columns are named key, user_mails and json_adress (sic!). emails or jsons are just invalid references.
Your absolutely basic query should be:
"SELECT * FROM public.account_recover_users WHERE emails = '$email'"
You can rename columns in the query by appling column aliases, but your WHERE clause cannot refer to output columns (aliases), it has to refer to input columns:
"SELECT key, user_mails AS email, json_adress AS jsons
FROM public.account_recover_users
WHERE user_mails = '$email'"
Detailed explanation in this related answer:
How to re-use result for SELECT, WHERE and ORDER BY clauses?
Because you're referencing public.account_recover_users in a derived table (in brackets), you need to give the derived table an alias which can be almost anything.
$existent= "SELECT * FROM
(SELECT public.account_recover_users.* AS keys,emails,jsons
FROM public.account_recover_users) as aru
WHERE emails='$email'";
In your case though I don't think you need a derived table at all. You could just write.
$existent = "SELECT key as keys, user_mail as emails, json_address as jsons
FROM public.account_recover_users
WHERE emails='$email'";
You don't need the subquery... (Alias it if you actually need one).
Nor do you need public, since it's in your search path by default.
Nor do you need the fields here, since you're selecting them all.
SELECT * FROM account_recover_users WHERE user_mails='$email'
Oh, and don't forget to escape $email... Look into PDO::prepare().

Should I be using multiple SQL queries or multiple loops in my client code?

I'm building a small internal phone number extension list.
In the database I have two tables, a people table, and a numbers table. The relationship is a one-to-many respectively.
I want to display the results in a single HTML table with one person per row but if they have multiple numbers it shows those in a single column with a rowspan on the person row to compensate.
Now, to get the results from the database to work with, I can either do:
(pseudocode)
SELECT id, name
FROM people
foreach result as row {
SELECT number
FROM numbers
WHERE numbers.person_id = row['id']
}
This would mean that I'm doing one query to get all users, but then if I have 100 users, I'm performing 100 additional queries to get the numbers for each user.
Instead I could do it like this:
(pseudocode)
SELECT number, person_id
FROM numbers
SELECT id, name
FROM people
foreach people as person {
echo name
foreach numbers as number {
if (number.id = person.id) {
echo number
}
}
}
So, essentially it is doing the exact same thing except instead I do two queries to get all the results into arrays and then loop through the arrays to format my tables.
Which method should I be using or is there a better way to do this?
The common way is to do a regular JOIN:
SELECT id, name, number
FROM people, numbers
WHERE people.id = numbers.person_id;
You can either add an ORDER BY to get the numbers in order, or you could create an array with a single pass over the resultset, and then loop through that array.
You can also consider a GROUP_CONCAT to concatenate all the numbers for the same person:
SELECT id, name, GROUP_CONCAT(number SEPARATOR ', ')
FROM people, numbers
WHERE people.id = numbers.person_id
GROUP BY people.id;
Since you are even asking this question: I cannot stress the fact that you should pick up an introductory book on database design. It helped me wonders to learn the theories behind relational databases.
You probably want to execute just one query. Something like
select people.id, people.name, group_concat(numbers.number)
from people
inner join numbers on numbers.id = people.id
group by people.id, people.name
order by people.name
Then you can loop over the result set with simple php code.
It depends, and you may have to time it to find out. Doing multiple queries is a lot of network turns if your database is on a different computer than your web server, so often this takes longer. However, if your database server is on the same computer as your web server, this might not be an issue.
Also consider the time it will take to look up the number in the array. As an array you are doing a linear order O(N) search. If you can put it in a hash, the lookup will be much faster, and the two query approach may be faster, but not if you spend a lot of time looking up the answer in your array.
Using a join to get it into one query, may be fastest, as the numbers will be associated with the people, depending on your container structure you are storing the data into to be accessed in your foreach loop.
Use a stored procedure or function to retrive the data, don't wite the sql in your programm
You should do neither. You can do one query (join) over the tables:
select name, number
from people p, numbers n
where p.id = n.person_id
order by name, number;
and then just one loop:
$link = mysqli_connect(...);
$query = 'select name, number from people p, numbers n where p.id = n.person_id order by name, number';
if ($result = mysqli_query($link, $query)) {
$person = '';
while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
if ($row['name'] !== $person) {
$person = $row['name'];
echo $person;
}
echo $row['number'];
}
}

Categories