adding and multiplying COUNT() of several tables - php

Is it possible to add and multiply the count of different tables where the id is the same?
Imagine:
Table_1 Table_2 Table_3
id id id
1 1 1
1 2 2
2 2 3
3 2 3
3 2 3
3 3 3
So that the end result would be this table with 2 columns:
id (COUNT(Table_1.id) + 2*COUNT(Table_2.id) + 3*COUNT(Table_3.id))
1 7
2 12
3 17

I don't know if I understood you correctly but give this a try,
SELECT a.ID,
a.aa + (2 * b.bb) + (3 * c.cc)
FROM
(
SELECT ID, COUNT(*) aa
FROM table1
GROUP BY ID
) a LEFT JOIN
(
SELECT ID, COUNT(*) bb
FROM table2
GROUP BY ID
) b ON a.ID = b.ID
LEFT JOIN
(
SELECT ID, COUNT(*) cc
FROM table3
GROUP BY ID
) c ON a.ID = c.ID
SQLFiddle Demo

SELECT id, counts_1.number + 2 * counts_2.number + 3 * counts_3.number
FROM
(SELECT id, COUNT(*) AS number FROM Table_1 GROUP BY id) AS counts_1
JOIN
(SELECT id, COUNT(*) AS number FROM Table_2 GROUP BY id) AS counts_2 USING (id)
JOIN
(SELECT id, COUNT(*) AS number FROM Table_3 GROUP BY id) AS counts_3 USING (id)
Note that this solution requires that every id exists at least once in each of the tables, otherwise it will be left out of the result. Changing this would require a FULL OUTER JOIN that MySQL is incapable of. There are ways around that limitation, though.

Related

MySQL Joins with "tolerance"

I would like to add some kind of "tolerance" to the following query. That means, that I can specify a value which expresses how many of the four (sub) selects return rows > 0. So if this value is 2, I only want to join these two tables. Is there a way to realize that?
SELECT distinct(user_id) FROM
(SELECT user_id FROM table1 WHERE ...) as t1
INNER JOIN
(SELECT user_id FROM table1 WHERE ...) as t2
ON t1.user_id=t2.user_id
INNER JOIN
(SELECT user_id FROM table1 WHERE ...) as t3
ON t1.user_id=t3.user_id
INNER JOIN
(SELECT user_id FROM table1 WHERE ...) as t4
ON t1.user_id=t4.user_id
EDIT:
Possible results for each sub-query could be as follows:
t1 t2 t3 t4
0 0 0
1 1 1 1
2 2 2 2
3 3
If all these sub results are joined it would result in: 1,2.
If I add a tolerance factor of 1, I want my result to be 0,1,2 as only one "0" is missing. If the factor was 2, the result would be 0,1,2,3 because two "3" and one "0" are missing. I hope this makes it clearer.
If i had understood your problem, you can add a variable in your sub-select and filter after:
SELECT distinct(user_id) FROM
(SELECT user_id, 1 as table_from FROM table1 WHERE ...) as t1
INNER JOIN
(SELECT user_id, 2 as table_from FROM table1 WHERE ...) as t2
ON t1.user_id=t2.user_id
INNER JOIN
(SELECT user_id, 3 as table_from FROM table1 WHERE ...) as t3
ON t1.user_id=t3.user_id
INNER JOIN
(SELECT user_id, 4 as table_from FROM table1 WHERE ...) as t4
ON t1.user_id=t4.user_id
WHERE table_from <= 2;
The solution was to union all sub selects and count them like follows:
SELECT distinct(user_id), sum(t) as tolerance FROM (
SELECT user_id, 1 as t FROM table1 WHERE ... GROUP BY...
UNION ALL
SELECT user_id, 1 as t FROM table1 WHERE ... GROUP BY...
UNION ALL
SELECT user_id, 1 as t FROM table1 WHERE ... GROUP BY...
UNION ALL
SELECT user_id, 1 as t FROM table1 WHERE ... GROUP BY...
) as x GROUP BY ... HAVING tolerance <= 2
Then you can specify how many sub selects should return something (here: 2).

left join two tables such that two columns of table 2 does not exist in table1

I have two tables table1(userid,regid) and table2(userid,hostuserid,status) where either userid or hostuserid equals myuserid(already contained in a variable $userid).I want to find all rows in table1 such that table1.userid!=table2.userid AND table1.userid!=table2.userid.
table1
--------
userid regid
1 gbjnknnk
2 bvgcghb
3 bjbnjb
table2
-------
userid hostuserid
1 5
5 2
$userid=5
query should return only one row=> 3 bjbnjb
how to implement the same.
what i had tried
SELECT * FROM table1 JOIN table2 WHERE (table1.userid!=table2.userid AND
table2.hostuserid=$userid) AND (table1.userid!=table2.hostuserid AND
table2.userid=$userid)
You could use a not in on a subselect with union
select userid
from table1
where user1 not in (
select userid
from table2
union
select
hostuserid from table2
)

Multiple WHERE conditions in 2 tables in MySQL

I have 2 tables as follows:
table1
ID Name Test
A011 John 1
A012 Lynda 1
A013 Micheal 1
A014 Jack 0
A021 Joe 1
A015 Paul 0
table2
ID Done
A011 1
A012 1
I want to select all rows from table1 that have an ID where the 3 first letters are equal to A01, and the test field is 1, and also the ID is not present in table2.
I tried this query:
SELECT a.* FROM table1 a LEFT JOIN table2 b ON a.ID = b.ID
WHERE a.test = 1 AND b.ID IS NULL
The result from that is 2 rows with ID A013 and A021. I tried to use LEFT(ID,3) to get the ID with A01, however, I couldn't achieve what I want.
How can I filter only the records where the ID starts with A01?
Try this, it will give you the desired result
SELECT t1.* FROM table1 t1 LEFT JOIN table2 t2 ON t1.userid = t2.userid WHERE LEFT(t1.userid , 3) LIKE '%A01%' AND t1.userid NOT IN (SELECT userid from table2)
SELECT * FROM table1
WHERE test = 1
AND ID LIKE "AO1%"
AND ID NOT IN (SELECT ID from table2)

mysql multiple COUNT() from multiple tables with LEFT JOIN

I want to show the conclusion of all users.
I have 3 tables.
table post
post_id(index) user_id
1 1
2 3
3 3
4 4
table photo
photo_id(index) user_id
1 2
2 4
3 1
4 1
table video
photo_id(index) user_id
1 4
2 4
3 3
4 3
and in table user
user_id(index) user_name
1 mark
2 tommy
3 john
4 james
in fact, it has more than 4 rows for every tables.
I want the result like this.
id name post photo videos
1 mark 1 2 0
2 tommy 0 1 0
3 john 2 0 2
4 james 1 1 2
5 .. .. .. ..
Code below is SQL that can work correctly but very slow, I will be true appreciated if you help me how it using LEFT JOIN for it. Thanks.
SQL
"select user.*,
(select count(*) from post where post.userid = user.userid) postCount,
(select count(*) from photo where photo.userid = user.userid) photoCount,
(select count(*) from video where video .userid = user.userid) videoCount
from user order by user.id"
(or ORDER BY postCount, photoCount or videoCount ASC or DESC as i want )
I done researched before but no any helped me.
SELECT u.user_id,
u.user_name,
COUNT(DISTINCT p.post_id) AS `postCount`,
COUNT(DISTINCT ph.photo_id) AS `photoCount`,
COUNT(DISTINCT v.video_id) AS `videoCount`
FROM user u
LEFT JOIN post p
ON p.user_id = u.user_id
LEFT JOIN photo ph
ON ph.user_id = u.user_id
LEFT JOIN video v
ON v.user_id = u.user_id
GROUP BY u.user_id
ORDER BY postCount;
Live DEMO
Your method of doing this is quite reasonable. Here is your query:
select user.*,
(select count(*) from post where post.userid = user.userid) as postCount,
(select count(*) from photo where photo.userid = user.userid) as photoCount,
(select count(*) from video where video.userid = user.userid) as videoCount
from user
order by user.id;
For this query, you want the following indexes:
post(userid)
photo(userid)
video(userid)
user(id)
You probably already have the last one, because user.id is probably the primary key of the table.
Note that a left join approach is a bad idea in this case. The three tables -- posts, photos, and videos -- are independent of each other. If a user has five of each, then joining them together would produce 125 intermediate rows. If a user has fifty of each, it would be 125,000 -- a lot of extra processing.
Your answer is probably slow as it is using a correlated sub-query i.e. the sub query is running once for each user_id (unless the optimizer is doing something smart - which shouldn't be counted on).
You could use a left outer join and count or use something temporary like:
SELECT u.user_id,
u.user_name,
ph.user_count AS 'photoCount',
p.user_count AS 'postCount',
v.user_count AS 'videoCount'
FROM user u
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM photo
GROUP BY user_id
) ph
ON ph.user_id=u.user_id
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM post
GROUP BY user_id
) p
ON p.user_id=u.user_id
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM video
GROUP BY user_id
) v
ON v.user_id=u.user_id
There are pros and cons for both (depending on indexes). Always have a look at the query plan (using EXPLAIN for MySQL).

how can i get a temp table result from a string?

for example:
my products best sort string is "'8207,17631,16717,18545,9062,17469,17246,17750"
this string is posted from php , I dont want to store them in datebase. I want to query datas from mysql and left join a temp table then sort by the temp table'sort.
how can I get this temp table from a string ?
my codes seems will be like that bellow:(wrong codes)
select
p.products_id
from
(
select '18207,17631,16717,18545,9062,17469,17246,17750' as products_id
) as p
order by p.sort
Your best approach could be - to use UNION for generating row set from string. This, however, will require joining your string in your application, like this:
$string = '18207,17631,16717,18545,9062,17469,17246,17750';
$id = 0;
$sql = join(' UNION ALL '.PHP_EOL, array_map(function($item) use (&$id)
{
return 'SELECT '.(++$id).' AS sort, "'.$item.'" AS products_id';
}, explode(',', $string)));
-end result will be like:
SELECT 1 AS sort, "18207" AS products_id UNION ALL
SELECT 2 AS sort, "17631" AS products_id UNION ALL
SELECT 3 AS sort, "16717" AS products_id UNION ALL
SELECT 4 AS sort, "18545" AS products_id UNION ALL
SELECT 5 AS sort, "9062" AS products_id UNION ALL
SELECT 6 AS sort, "17469" AS products_id UNION ALL
SELECT 7 AS sort, "17246" AS products_id UNION ALL
SELECT 8 AS sort, "17750" AS products_id
However, if you want to do that in SQL - that will not be easy, since MySQL doesn't supports sequences - and, therefore, you'll need to use some tricks to produce desired rows set. There's a way to generate N consecutive numbers with:
SELECT id+1
FROM
(SELECT
(two_1.id + two_2.id + two_4.id +
two_8.id + two_16.id) AS id
FROM
(SELECT 0 AS id UNION ALL SELECT 1 AS id) AS two_1
CROSS JOIN (SELECT 0 id UNION ALL SELECT 2 id) AS two_2
CROSS JOIN (SELECT 0 id UNION ALL SELECT 4 id) AS two_4
CROSS JOIN (SELECT 0 id UNION ALL SELECT 8 id) AS two_8
CROSS JOIN (SELECT 0 id UNION ALL SELECT 16 id) AS two_16
) AS init
LIMIT 10
-this will result in 10 numbers 1..10 (check this fiddle). Using this, you can get your end result:
SELECT
ELT(id+1, 18207,17631,16717,18545,9062,17469,17246,17750) AS products_id,
id+1 AS sort
FROM
(SELECT
(two_1.id + two_2.id + two_4.id +
two_8.id + two_16.id) AS id
FROM
(SELECT 0 AS id UNION ALL SELECT 1 AS id) AS two_1
CROSS JOIN (SELECT 0 id UNION ALL SELECT 2 id) AS two_2
CROSS JOIN (SELECT 0 id UNION ALL SELECT 4 id) AS two_4
CROSS JOIN (SELECT 0 id UNION ALL SELECT 8 id) AS two_8
CROSS JOIN (SELECT 0 id UNION ALL SELECT 16 id) AS two_16
) AS init
HAVING
products_id IS NOT NULL
-check this fiddle. However, this may be slow and I recommend you to use your application layer to build desired SQL.
something like this? use UNION to generate inline view. this can be generated by Client side.
SELECT *
FROM
(
SELECT '18207' AS products_id, 1 as sort
UNION
SELECT '17631' AS products_id, 2 as sort
UNION
SELECT '16717' AS products_id, 3 as sort
UNION
SELECT '18545' AS products_id, 4 as sort
UNION
SELECT '9062' AS products_id, 5 as sort
) x JOIN tbl x.products_id = tbl.products_id
ORDER BY sort

Categories