How to collect the COUNT(*) of two Distinct queries? - php

I tried the following query and got an error in MySQL. I kind of know why it's throwing me a syntax error (it's related to the two DISTINCT queries being run on the same line) but still believe that its possible (somehow). The two columns card_type and split_type each contain one of five values ("Attack", "Foundation", "Character", "Asset", "Action"). What I would like to do is create a query that would count a record as a +1 if "Attack" appeared in either card_type or split_type.
SELECT DISTINCT(ufs.card_type), DISTINCT(ufs.split_type), COUNT(*) AS COUNT
FROM jg1_products p
LEFT JOIN jg1_product_types pt ON p.products_type = pt.type_id
LEFT JOIN jg1_products_to_categories ptc ON p.products_id = ptc.products_id
LEFT JOIN jg1_cards_ufs ufs ON ufs.products_id = p.products_id
WHERE type_handler LIKE "%product_cards%"
AND ptc.categories_id = 89
GROUP BY ufs.card_type
I guess I should be a BIT clearer in my explanation:
I was hoping that the SQL query would return the total number of results which have met a condition in either Column A or Column B.
EXAMPLE:
If the word "Attack" appeared in either column A or column B, count that as one.
If the word "Attack" appeared in Column A but Column B contains the word "Foundation", That would be +1 to Attack and +1 to Foundation.
In the end, the function/SQL would return to number of times that word (either of the five possibles) would appear between those two columns. So in short I used the "DISTINCT" command (incorrect I'll add) so that all the distinct values in column A are returned and the same for B. If they match, count that as one.

Q: "a query that would count a record as a +1 if "Attack" appeared in either card_type or split_type."
A: It's not clear what resultset you want to return.
EDIT
Based on your comment/clarification, if I'm understanding this correctly, if the rows returned by the SELECT were something like this example:
card_type split_type
--------- -----------
Attack Attack
Attack Foundation
Attack Crescendo
Foundation Foundation
You want a resultset something like this:
Attack 3
Foundation 2
Crescendo 1
You want 3 returned for "Attack", because three rows had the value 'Attack' in either card_type or split_type. That is, you don't want to return a count of 4, the number of times the value appeared.
To get that result, using a COUNT aggregate, I would run this as two separate queries, and combine the results of the two queries using a UNION ALL set operator. The first query would get a count by just card_type, the second query would get a count by split_type. The "trick" would be for the second query to exclude any rows where the split_type matches the card_type.
The two combined queries would be used as an inline view, the outer query would combine the separate counts using a SUM() aggregate function.
I would do the query using a form something like this:
SELECT c.type
, SUM(c.cnt) AS cnt
FROM ( SELECT ufs.card_type AS `type`
, COUNT(1) AS cnt
FROM ...
GROUP BY ufs.card_type
UNION ALL
SELECT ufs.split_type AS `type`
, COUNT(1) AS cnt
FROM ...
AND NOT (ufs.split_type <=> ufs.card_type)
GROUP BY ufs.split_type
) c
GROUP BY c.type
You'd plug in the row source of the original query two times, replacing the ... in the query above.
Previous answer:
Assuming that you have a SELECT that returns the rows you want checked, one "trick" is to use an expression in the SELECT list to perform a conditional test, and return either a zero or one, and then use SUM() aggregate to return a "count" of the records that meet the specification..
SELECT SUM(IF(ufs.card_type LIKE '%Attack%' OR ufs.split_type LIKE '%Attack%',1,0)) AS cnt
FROM jg1_products p
LEFT JOIN jg1_product_types pt ON p.products_type = pt.type_id
LEFT JOIN jg1_products_to_categories ptc ON p.products_id = ptc.products_id
LEFT JOIN jg1_cards_ufs ufs ON ufs.products_id = p.products_id
WHERE type_handler LIKE "%product_cards%"
AND ptc.categories_id = 89
This query returns a single row, unlike your original query that returns multiple rows. (Again, it's not clear what resultset you want returned; if you actually want to return a count for each distinct card_type, which would be returned if we included a GROUP BY ufs.card_type clause.
MySQL also provides a convenient shorthand for the boolean expression: the evaluation of a boolean expression returns 1 if TRUE, 0 if FALSE, and NULL if NULL. So this expression:
SELECT SUM(ufs.card_type LIKE '%Attack%' OR ufs.split_type LIKE '%Attack%')
FROM ...
is equivalent to the expression in the query above, except for the handling of NULL values.
It's not clear whether you want to check if the column "contains" the string 'Attack' as part of the string, or is the entire string; to check if the value of the column is exactly equal to 'Attack', use the equality comparison instead of LIKE
SELECT SUM(ufs.card_type = 'Attack' OR ufs.split_type = 'Attack') AS cnt
FROM ...
NOTE
DISTINCT is not a function, it's a keyword.
The valid syntax is SELECT DISTINCT expr1, expr2, ... exprN FROM ....
It's invalid to include the DISTINCT keyword multiple times after the SELECT keyword, or in a position other than immediately following SELECT. (The DISTINCT keyword can also be included the COUNT() aggregate function, e.g. SELECT COUNT(DISTINCT expr), but that's entirely different than SELECT DISTINCT.
The parens are entirely ignored. That is, SELECT DISTINCT(foo) is identical to SELECT DISTINCT foo. Including parens is entirely unnecessary, and makes it look like DISTINCT is a function (which it is not.)

You can try below query
SELECT DISTINCT ufs.card_type, ufs.split_type, COUNT(*) AS COUNT
FROM jg1_products p
LEFT JOIN jg1_product_types pt ON p.products_type = pt.type_id
LEFT JOIN jg1_products_to_categories ptc ON p.products_id = ptc.products_id
LEFT JOIN jg1_cards_ufs ufs ON ufs.products_id = p.products_id
WHERE type_handler LIKE "%product_cards%"
AND ptc.categories_id = 89
GROUP BY ufs.card_type
here query will select unique records from card_type and split_type
For more Link can help you.

try this:
SELECT COUNT(DISTINCT(ufs.rarity)) AS COUNT ...
Enjoy your code!

Related

SQL - Select ALL rows that are LIKE%...% of each other (All Similar Rows)

For example, lets say in column source I have the following entries
SourceFileEmbed122
SourceFile1333
SourceItem13366
PreLoadSource7755
And I do a query of SourceFile it should match row 1 and 2 and show me all the column data for that row, but if I search for example: PreLoadSource or SourceItem it shouldnt show anything, as there is only 1 row that has a similar entry.
Kinda like an if contains sort of thing.
Basically, I want to do something like:
SELECT source, COUNT(*) TotalCount FROM sources GROUP BY source HAVING COUNT(*) > 1 ORDER BY COUNT(*) DESC
But the query does LIKE instead of LIKE%...% (Like in PHPMyAdmin) which results in it only matching EXACT matches of each other, so stuff like:
row123/
row123
Wont match each other and will be ignored. But I want this to MATCH basically if row123's full text is ALSO all in another row's value, then match.
Lets say I have:
http://link.ext/dir123/file.mp3
http://link.ext/dir123
http://link.ext/dir123/file2.mp3
http://link.ext/dir123
The query should match .../file.mp3, .../file2.mp3 and ../dir123 because row 2 http://link.ext/dir123 is also in row 1, 3 and 4.
One way is doing a inner join with the same table,
if you need a simple count you can do something like that:
SELECT s1.source, COUNT(*)
FROM sources s1
INNER JOIN sources s2
ON s1.id <> s2.id AND s1.source LIKE CONCAT('%', s2.source, '%')
GROUP BY s1.source
One way to test for at least two matches is:
select s.*
from sources s
where s.source like '%<whatever>%' and
exists (select 1
from source s2
where s2.source like '%<whatever>%' and
s2.source <> s.source
);

What structure must be mysql indexed for my inner join sql statement?

I have nearly 1 million records for my firma table and this statement
SELECT firma.adi,firma.tel, firma.cep, firma.adres,
firma.etiket, sehir.ad, ilce.ilce_adi, ustkat.ust_adi
FROM firma
inner join sektor on sektor.s_fid=firma.id
inner join ustkat on ustkat.ust_id=sektor.s_ustid
inner join sehir on sehir.id=firma.sehir
inner join ilce on ilce.ilceid=firma.ilce
WHERE firma.uyeliktur<4 and firma.onay=1 and
firma.zoom>0 and firma.koordinat>0
GROUP BY firma.id
ORDER BY firma.uyeliktur desc, firma.bastarih desc
how can i use mysql index for this.
You would want an index with all the columns in the where clause. The first should be onay (because of the equality condition). The second should be whichever condition is most restrictive. One of these:
firma(onay, uyeliktur, zoom, koordinat)
firma(onay, zoom, uyeliktur, koordinat)
firma(onay, koordinat, uyeliktur, zoom)
Only the first two columns are used directly for looking up the rows for the where. The other two are a slight efficiency so the where values are there.
If, by the way, these are actually flags, you are better off saying zoom = 1 than zoom > 0.

Get values from database where column is unique

I need help with an advanced SQL-query (MSSQL 2000).
I have a table called Result that lists athletics 100 meter race-times. A runner can have several racetimes but I want to show only the best time from each runner.
The Result-table contains three columns, Result_id, athlete_id, result_time. So athlete_id must be unique when I list the values and result_time must be the fastest (lowest) value.
Any ideas?
In SQL Server 2000, you can't use windows functions. You can do this as follows:
select r.*
from result r join
(select athlete_id, min(result_time) as mintime
from result r
group by athlete_id
) rsum
on rsum.athlete_id = r.athlete_id and r.time = rsum.mintime
In more recent versions of SQL Server, you would use row_number().
If you simply need the fastest time for each athlete_id, do this:
select athelete_id, min(result_time) as FastestTime
from result
group by athelete_id
To show additional columns from the result table, you can join back to it like this:
select r.*
from result r
inner join (
select athelete_id, min(result_time) as FastestTime
from result
group by athelete_id
) rm on r.athelete_id = rm.athelete_id and r.result_time = rm.FastestTime
What you want is to use an aggregate function. in this case min() which will select the minumin data from all the rows that have the same data in the other selected columns. This means you also have to us the group by clause. The query below should give you the results you want.
Edit: If you need other columns, just bring them into the select clause, then add them to the group by clause like below:
select althlete_id, result_id, min(result_time) as result_time from result-table group by althlete_id, result_id
select althlete_id, result_id, min(result_time) as result_time, race_date from result-table group by althlete_id, race_date, result_id
Edit: You need to add all the columns into the group by that aren't part of an aggregate function. Aggregate functions are ones like min(), max(), avg() and so on.
Short answer: If you aren't putting a column in brackets, it probably has to be in the group by.

MySQL Query AND IN (select.... - Need assistance in clarifying and is it a Sub Routine

Can you let me know if my interpretation is correct (the last AND part)?
$q = "SELECT title,name,company,address1,address2
FROM registrations
WHERE title != 0 AND id IN (
SELECT registrar_id
FROM registrations_industry
WHERE industry_id = '$industryid'
)";
Below was really where I am not sure:
... AND id IN (select registrar_id from registrations_industry where industry_id='$industryid')
Interpretation: Get any match on id(registrations id field) equals registrar_id(field) from the join table registrations_industry where industry_id equals the set $industryid
Is this select statement considered a sub routine since it's a query within the main query?
So an example would be with the register table id search to 23 would look like:
registrations(table)
id=23,title=owner,name=mike,company=nono,address1=1234 s walker lane,address2
registrations_industry(table)
id=256, registrar_id=23, industry_id=400<br>
id=159, registrar_id=23, industry_id=284<br>
id=227, registrar_id=23, industry_id=357
I assume this would return 3 records with the same registration table data And of course varying registrations_industry returns.
For a given test data set your query will return one record. This one:
id=23,title=owner,name=mike,company=nono,address1=1234 s walker lane,address2
To get three records with the same registration table data and varying registrations_industry you need to use JOIN.
Something like this:
SELECT r.title, r.name, r.company, r.address1, r.address2
FROM registrations AS r
LEFT OUTER JOIN registrations_industry AS ri
ON ri.registrar_id=r.id
WHERE r.title!=0 AND ri.industry_id={$industry_id}
Sorry for the essay, I didn't realize it was as long as it is until looking at it now. And although you've checked an answer, I hope you read this gain some insight into why this solution is preferred and how it evolved out of your original query.
First things first
Your query
$q = "SELECT title,name,company,address1,address2
FROM registrations
WHERE title != 0 AND id IN (
SELECT registrar_id
FROM registrations_industry
WHERE industry_id = '$industryid'
)";
seems fine. The IN syntax is equivalent to a number of OR matches. For example
WHERE field_id IN (101,102,103,105)
is functionally equivalent to
WHERE (field_id = 101
OR field_id = 102
OR field_id = 103
OR field_id = 105)
You complicate it a bit by introducing a subquery, no problem. As long as your subquery returns one column (and yours does), passing it to IN will be fine.
In your case, you're comparing registrations.id to registrations_industry.registrar_id. (Note: This is just <table>.<field> syntax, nothing special, but helpful to disambiguate what tables your fields are in.)
This seems fine.
What happens
SQL would first run the subquery, generating a result set of registrar_ids where the industry_id was set as specified.
SQL would then run the outer query, replacing the subquery with its results and you would get rows from registrations where registrations.id matched one of the registrar_ids returned from the subquery.
Subqueries are helpful to debug your code, because you can pull out the subquery and run it separately, ensuring its output is as you expect.
Optimization
While subqueries are good for debugging, they're slow, at least slower than using optmized JOIN statements.
And in this case, you can convert your query to a single-level query (without subqueries) by using a JOIN.
First, you'd start with basically the exact same outer query:
SELECT title,name,company,address1,address2
FROM registrations
WHERE title != 0 AND ...
But you're also interested in data from the registrations_industry table, so you need to include that. Giving us
SELECT title,name,company,address1,address2
FROM registrations, registrations_industry
WHERE title != 0 AND ...
We need to fix the ... and now that we have the registrations_industry table we can:
SELECT title,name,company,address1,address2
FROM registrations, registrations_industry
WHERE title != 0
AND id = registrar_id
AND industry_id = '$industryid'
Now a problem might arise if both tables have an id column -- since just saying id is ambiguous. We can disambiguate this by using the <table>.<field> syntax. As in
SELECT registrations.title, registrations.name,
registrations.company, registrations.address1, registrations.address2
FROM registrations, registrations_industry
WHERE registrations.title != 0
AND registrations_industry.industry_id = '$industryid'
We didn't have to use this syntax for all the field references, but we chose to for clarity. The query now is unnecessarily complex because of all the table names. We can shorten them while still providing disambiguation and clarity. We do this by creating table aliases.
SELECT r.title, r.name, r.company, r.address1, r.address2
FROM registrations r, registrations_industry ri
WHERE r.title != 0
AND ri.industry_id = '$industryid'
By placing r and ri after the two tables in the FROM clause, we're able to refer to them using these shortcuts. This cleans up the query but still gives us the ability to clearly specify which tables the fields are coming from.
Sidenote: We could be more explicit about the table aliases by including the optional AS e.g. FROM registrationsASr rather than just FROM registrations r, but I typically reserve AS for field aliases.
If you run the query now you will get what is called a "Cartesian product" or in SQL lingo, a CROSS JOIN. This is because we didn't define any relationship between the two tables when, in fact, there is one. To fix this we need to reintroduce part of the original query that was lost: the relationship between the two tables
r.id = ri.registrar_id
so that our query now looks like
SELECT r.title, r.name, r.company, r.address1, r.address2
FROM registrations r, registrations_industry ri
WHERE r.title != 0
AND r.id = ri.registrar_id
AND ri.industry_id = '$industryid'
And this should work perfectly.
Nitpicking -- implicit vs. explicit joins
But the nitpicker in me needs to point out that this is called an "implicit join". Basically you're joining tables but not using the JOIN syntax.
A simpler example of an implicit join is
SELECT *
FROM foo f, bar b
WHERE f.id = b.foo_id
The corresponding explicit syntax is
SELECT *
FROM foo f
JOIN bar b ON f.id = b.foo_id
The result will be identical but it is using proper (and clearer) syntax. (Its clearer because it explicitly stats that there is a relationship between the foo and bar tables and it is defined by f.id = b.foo_id.)
We could similarly express your implicit query
SELECT r.title, r.name, r.company, r.address1, r.address2
FROM registrations r, registrations_industry ri
WHERE r.title != 0
AND r.id = ri.registrar_id
AND ri.industry_id = '$industryid'
explicitly as follows
SELECT r.title, r.name, r.company, r.address1, r.address2
FROM registrations r
JOIN registrations_industry ri ON r.id = ri.registrar_id
WHERE r.title != 0
AND ri.industry_id = '$industryid'
As you can see, the relationship between the tables is now in the JOIN clause, so that the WHERE and subsequent AND and OR clauses are free to express any restrictions. Another way to look at this is if you took out the WHERE + AND/OR clauses, the relationship between tables would still hold and the results would still "make sense" whereas if you used the implicit method and removed the WHERE + AND/OR clauses, your result set would contain rows that were misleading.
Lastly, the JOIN syntax by itself will cause rows that are in registrations, but do not have any corresponding rows in registrations_industry to not be returned.
Depending on your use case, you may want rows from registrations to appear in the results even if there are no corresponding entries in registrations_industry. To do this you would use what's called an OUTER JOIN. In this case, we want what is called a LEFT OUTER JOIN because we want all of the rows of the table on the left (registrations). We could have alternatively used RIGHT OUTER JOIN for the right table or simply OUTER JOIN for the outer join of both tables.
Therefore our query becomes
SELECT r.title, r.name, r.company, r.address1, r.address2
FROM registrations r
LEFT OUTER JOIN registrations_industry ri ON r.id = ri.registrar_id
WHERE r.title != 0
AND ri.industry_id = '$industryid'
And we're done.
The end result is we have a query that is
faster in terms of runtime
more compact / concise
more explicit about what tables the fields are coming from
more explicit about the relationship between the tables
A simpler version of this query would be:
SELECT title, name, company, address1, address2
FROM registrations, registrations_industry
WHERE title != 0
AND id = registrar_id
AND industry_id = '$industryid'
Your version was a subquery, this version is a simple join. Your assumptions about your query are generally correct, but it is harder for SQL to optimize and a little harder to unravel to anyone trying to read the code. Also, you won't be able to extract the data from the registrations_industry table in that parent SELECT statement because it's not technically joining and the subtable is not a part of the parent query.

PHP/MySQL - Counting items within a query

I have the following results for my database table:
The Query:
SELECT
service_titles.user_id, service_titles.slide_id, service_titles.name as title_name ,service_names.name as service_name
FROM service_names
INNER JOIN service_titles ON service_names.title_id = service_titles.id
So what needs to happen is:
If the user has 2 unique service titles, then the max number of service_names for that title will be 6
If the user has 1 service title, the the max number of service_names for that title will be 16
I will be using PHP for all of the coding, but I am wondering how I would go about this. I need a way to count how many unique service_titles there are for that user and slide, and then count how many service items there are for each title.
Thanks for any help!
SELECT COUNT(DISTINCT service_titles.name)
FROM service_names
INNER JOIN service_titles ON service_names.title_id = service_titles.id
GROUP BY service_titles.user_id, service_titles.slide_id
That'll get you the number of distinct title_names for each user_id/slide_id combo.
SELECT COUNT(DISTINCT service_names.name)
FROM service_names
INNER JOIN service_titles ON service_names.title_id = service_titles.id
GROUP BY service_titles.user_id, service_titles.slide_id
... and that's the number of distinct service_names for same. If you want both in one query, you can put both COUNTs together, since you're using the same GROUP BY regardless.
You could use a CASE statement within your query to change the max number of service_names.
See MySQL CASE statement reference
To do this in the SQL itself would be quicker than evaluating it in PHP.
To count how many distinct titles you can try:
SELECT user_id, COUNT(DISTINCT name)
FROM service_titles
GROUP BY user_id

Categories