MySQL: how can I count number of articles by a join table - mysql

I have a table with news items, I have another table with media_types, I want to make one simple query that reads the media_types table and count for each record how many news_items exist.
The result will be turned into a json response that I will use for a chart, this is my SQLstatement
SELECT
gc.country AS "country"
, COUNT(*) AS "online"
FROM default_news_items AS ni
JOIN default_news_item_country AS nic ON (nic.id = ni.country)
JOIN default_country AS c ON (nic.country = c.id)
JOIN default_geo_country AS gc ON (gc.id = c.geo_country)
LEFT JOIN default_medias ON (m.id = ni.media)
WHERE TRUE
AND ni.deleted = 0
AND ni.date_item > '2013-10-23'
AND ni.date_item < '2013-10-29'
AND gc.country <> 'unknown'
AND m.media_type = '14'
GROUP BY gc.country
ORDER BY `online` desc LIMIT 10
This is the json respond I create from the mysql respond
[
{"country":"New Zealand","online":"7"},
{"country":"Switzerland","online":"1"}
]
How do I add print and social data to my output like this
I would like the json respond look like this
[
{"country":"New Zealand","online":"7", "social":"17", "print":"2"},
{"country":"Switzerland","online":"1", "social":"7", "print":"1"}
]
Can I use the count (*) in the select statement to do something like this
COUNT( * ) as online, COUNT( * ) as social, COUNT( * ) as print
Is it possible or do I have to do several SQL statement to get the data I'm looking for?

This is the general structure:
SELECT default_geo_country.country as country,
SUM(default_medias.media_type = 14) as online,
SUM(default_medias.media_type = XX) as social,
SUM(default_medias.media_type = YY) as print
FROM ...
JOIN ...
WHERE ...
GROUP BY country

I think you want conditional aggregation. Your question, however, only shows the online media type.
Your query would be more readable by using table aliases and removing the back quotes. Also, if media_type is an integer, then you should not enclose the constant for comparison in single quotes -- I, for one, find it misleading to compare a string constant to an integer column.
I suspect this is the way you want to go. Where the . . . is, you want to fill in with the counts for the other media types.
SELECT default_geo_country.country as country,
sum(media_type = '14') as online,
sum(default_medias.media_type = XX) as social,
sum(default_medias.media_type = YY) as print
. . .
FROM default_news_items ni JOIN
default_news_item_country nic
ON nic.id = ni.country JOIN
default_country dc
ON nic.country = dc.id JOIN
default_geo_country gc
ON gc.id = dc.geo_country LEFT JOIN
default_medias dm
ON dm.id = dni.media
WHERE ni.deleted = '0'
AND ni.date_item > '2013-10-23'
AND ni.date_item < '2013-10-29'
AND gc.country <> 'unknown'
GROUP BY gc.country
ORDER BY online desc
LIMIT 10

Related

Search query for posts within certain period of time

The query below is searching for posts that are followed by users (if user follows category, user or topic).
It is working if it doesn't use $time_interval.
I would like to select posts from a certain period of time (last week, last month etc.).
I hardcoded 1 month and $filter for this example. But as soon as I try to select posts from last month, the query stops working.
I tried with methods that I used in other places, but this query is much more complex and by even putting the query to work to this point was very long process of errors, so I am not sure if it is written in the best possible way.
$current_user_id = get_current_user_id();
$term_relat_table = ;// table with term relationships
$topic_follow_table = ;// contains which topic user follows
$cat_follow_table = ;// contains which category user follows
$posts_table = ;// table with posts
$user_follow_user_table = ;// contains which users current user follows
$votes_stats_table = ;// table with votes
$perPage = 10; // hardcoded for this example
$orderby = "ORDER BY vote_rating DESC, total_votes DESC"; // hardcoded for this example
$time_interval = date('Y-m-d H:i:s', strtotime('-1 month')); // wasnt able to include in query
//I tried placing p.post_date > $time_interval in different places in query but none seem to work.
$all_results = $wpdb->get_results($wpdb->prepare("
SELECT v.topic_id AS post_id
, v.votes_rating AS vote_rating
, v.total_votes AS total_votes
FROM $votes_stats_table v
JOIN
( SELECT tf.discussion_id AS post_id
FROM $topic_follow_table AS tf
WHERE tf.current_user = %d
UNION ALL
SELECT p.ID AS post_id
FROM $user_follow_user_table AS uf
JOIN $posts_table AS p
ON p.post_author = uf.followed_user
WHERE uf.current_user = %d
-- tried placing p.post_date > %s here, and $time_interval below next to other variables
UNION ALL
SELECT r.object_id AS post_id
FROM $term_relat_table AS r
JOIN $cat_follow_table AS cf
ON cf.cat_id = r.term_taxonomy_id
WHERE cf.user_id = %d
) AS i
ON i.post_id = v.topic_id
GROUP
BY v.topic_id
$orderby
LIMIT %d
OFFSET $offset", $current_user_id, $current_user_id, $current_user_id, $perPage));

Include another select column based on data - MySQL

How do I include SUM((pm.Quantity * bl.TotalQty)) AS NextBOMItemCount WHERE projectbomlist.ParentPartNum = bl.PartNum?
The data should not be changed, the same data should be retrieved, the however additional column has to be included.
VIEW: `NEWprojectBOMItemCount
select
`pm`.`ProjectCode` AS `ProjectCode`,
`bl`.`PartNum` AS `PartNum`,
sum((`pm`.`Quantity` * `bl`.`TotalQty`)) AS `BOMItemCount`,
`bl`.`mp` AS `mp`,
`p`.`complete` AS `complete`,
`bl`.`RMInd` AS `RMInd`,
`bl`.`M_PartNum` AS `M_PartNum`
from
(
(`projectmachine` `pm` join `projectbomlist` `bl`)
join `projects` `p`
)
where
(
(`pm`.`MachineListID` = `bl`.`MachineListID`)
and (`pm`.`ProjectCode` = `bl`.`ProjectCode`)
and (`pm`.`ProjectCode` = `p`.`ProjectCode`)
and (`p`.`AfterProjectHeirarchyInd` = 'Y')
)
and and pm.ProjectCode = 'AB212323'
group by
`pm`.`ProjectCode` ,
`bl`.`PartNum`
order by
`pm`.`ProjectCode` ,
`bl`.`PartNum`
Or, another option can be, please consider above view used in below query, please suggest changes to the below query as shown above (repeating here)
`sum((pm.Quantity * bl.TotalQty)) AS NextBOMItemCount where projectbomlist.ParentPartNum = bl.PartNum` - in place of `(select-NextBOMItemCount)`?
Please see PBLH.ParentPartNum is the column that I should compare with BL.ProjectCode to get NextBOMItemCount value.
QUERY calling view: NEWprojectBOMItemCount
Select
BL.PartNum PartNumber,
PBLH.ParentPartNum NextBOM,
(select-NextBOMItemCount),
BOMItemCount TotalQty,
PL.Description,
BL.MP as PartType,
PL.Vendor,
PL.QBType
from
NEWprojectBOMItemcount BL,
bomwiz.partslist PL,
bomwiz.projectbomlistheirarchy PBLH
Where
BL.PartNum = PL.PartNum
And BL.PartNum = PBLH.PartNum
And BL.ProjectCode = PBLH.ProjectCode
And BL.projectCode = 'AB212323'
Order By PartNumber
I think that you are looking for conditional aggregation. Your requirement could be expressed as follows:
SUM(
CASE WHEN blh.ParentPartNum = bl.PartNum
THEN pm.Quantity * bl.TotalQty
ELSE 0
END
) AS NextBOMItemCount
Let me pinpoint other issues with your query:
you have unwanted parentheses all around, and I am suspicious about the syntax of the JOINs ; you need to move conditions to the ON clause of the relevant JOIN.
every non-aggregated column must appear in the GROUP BY clause - you have missing columns there
backquotes are usually not needed
Here is an updated version of the query:
SELECT
pm.ProjectCode AS ProjectCode,
bl.PartNum AS PartNum,
SUM(pm.Quantity * bl.TotalQty) AS BOMItemCount,
SUM(
CASE WHEN blh.ParentPartNum = bl.PartNum
THEN pm.Quantity * bl.TotalQty
ELSE 0
END
) AS NextBOMItemCount,
bl.mp AS mp,
p.complete AS complete,
bl.RMInd AS RMInd,
bl.M_PartNum AS M_PartNum
FROM
projectmachine AS pm
INNER JOIN projectbomlist AS bl
ON pm.MachineListID = bl.MachineListID
AND pm.ProjectCode = bl.ProjectCode
INNER JOIN join projects AS p
ON pm.ProjectCode = p.ProjectCode
AND p.AfterProjectHeirarchyInd = 'Y'
INNER JOIN projectbomlistheirarchy blh
ON bl.ProjectCode = blh.ProjectCode
WHERE
pm.ProjectCode = 'AB212323'
GROUP BY
pm.ProjectCode,
bl.PartNum,
bl.mp,
p.complete,
bl.RMInd,
bl.M_PartNum
ORDER BY
pm.ProjectCode,
bl.PartNum

Sql query to show only most recent message for specific user?

I have two sql tables one which has users and the other which has messages.
Right now my query is :
SELECT users.memberid,users.username,users.profileimage,users.gender,message.messagebody, message.fromid,message.toid,message.messageid
FROM message,users
WHERE message.fromid = users.memberid AND message.toid = '$id' AND recieverdeleted='0'
ORDER BY datetime DESC LIMIT 55
Right now what is being returned is all the information for everymessage regardless of formid(sender's id)
The thing is , I want to only display the most recent message for every from id ..Kind of like how Facebook only shows you the most recent message that friend x has sent you, and not all the messages. I will work on showing all messages after the user has clicked on his friend's most recent message.
Thanks
Well, you tried. That's good.
SELECT u.memberid
, u.username
, u.profileimage
, u.gender
, m.messagebody
, m.fromid
, m.toid
, m.messageid
FROM users u
JOIN message m
ON m.fromid = u.memberid
JOIN (SELECT fromid,toid,MAX(datetime) max_datetime FROM message GROUP BY fromid,toid) n
ON n.fromid = m.fromid
AND n.toid = m.toid
AND n.max_datetime = m.datetime
WHERE message.toid = $id
AND recieverdeleted = 0
ORDER
BY datetime DESC LIMIT 55;
At first join the two tables than apply order by because at this moment the command is not sure for which table you want to apply the ordering.
Or better you create an intermediate table from the selection of two tables and then apply the ordering.
Something like:
SELECT username,messagebody,fromid FROM(
SELECT users.memberid,users.username,users.profileimage,users.gender,message.messagebody, message.fromid,message.toid,message.messageid,message.datetime
FROM message,users
WHERE message.fromid = users.memberid AND message.toid = '$id' AND recieverdeleted='0' )INTERMEDIATE_TABLE ORDER BY datetime DESC
I might be wrong in syntax as I have done sql codes a long ago but you should try something very similar like this.
Try this its working
SELECT m1.*
FROM table_name m1
INNER JOIN (SELECT MAX(senddate) AS senddate,
IF(member_id2 = 3, member_id1, member_id2 ) AS user
FROM table_name
WHERE (member_id1 = 3 AND delete1=0) OR
(member_id2 = 3 AND delete2=0)
GROUP BY user) m2
ON m1.senddate = m2.senddate AND
(m1.member_id1 = m2.user OR m1.member_id2 = m2.user)
WHERE (member_id1 = 3 AND delete1=0) OR
(member_id2 = 3 AND delete2=0)
ORDER BY m1.senddate DESC
Try this::
SELECT users.memberid,users.username,users.profileimage,users.gender,message.messagebody, message.fromid,message.toid,message.messageid
FROM message inner join users on (message.fromid = users.memberid)
where message.toid = '$id' AND recieverdeleted='0'
ORDER BY message_datetime DESC limit 1

Unique records query

The following query is showing same user for multiple times, how to make it unique?
$query = 'SELECT a.connection_id, a.connect_from, a.connect_to,
b.userid, b.thumb, c.name, d.user_id, d.field_id,
d.value as bday, e.creator, e.id as videoprofile,
DATE_FORMAT(FROM_DAYS(DATEDIFF(NOW(),d.value)), "%Y")+0 AS age
FROM `#__community_connection` AS a
LEFT JOIN `#__community_users` AS b
ON a.connect_to = b.userid
LEFT JOIN `#__users` AS c ON a.connect_to = c.id
LEFT JOIN `#__community_fields_values` AS d
ON a.connect_to = d.user_id
LEFT JOIN `#__community_videos` AS e ON a.connect_to = e.creator
WHERE a.connect_from = "' . $uid .'" AND d.field_id = "3"
ORDER BY DAYOFMONTH( bday ) ASC';
Add DISTINCT after your SELECT to return only unique rows. So change the first line of your code to the following:
$query = 'SELECT DISTINCT a.connection_id, a.connect_from, a.connect_to, ...
[The rest of your query follows here.]
Not entirely sure what you're asking but I assume you want a unique identifacation method for your users?
Well in PHP you can hash their ip address and use that as their id, however it contains numbers and letters, so that'll make it unique i suppose :)
Here's how it'll look
.md5($data['post_ip']);
it'll appear different to everyone, for example, mine appears as the following
e36e263f082188a317f89e0dfef766ed
hopefully this is what you're looking for :)

Help me change this single complex query to use temporary tables

About the system:
- There are tutors who create classes and packs
- A tags based search approach is being followed.Tag relations are created when new tutors register and when tutors create packs (this makes tutors and packs searcheable). For details please check the section How tags work in this system? below.
Following is the concerned query
Can anybody help me suggest an approach using temporary tables. We have indexed all the relevant fields and it looks like this is the least time possible with this approach:-
SELECT SUM(DISTINCT( t.tag LIKE "%Dictatorship%"
OR tt.tag LIKE "%Dictatorship%"
OR ttt.tag LIKE "%Dictatorship%" )) AS key_1_total_matches
,
SUM(DISTINCT( t.tag LIKE "%democracy%"
OR tt.tag LIKE "%democracy%"
OR ttt.tag LIKE "%democracy%" )) AS key_2_total_matches
,
COUNT(DISTINCT( od.id_od )) AS
tutor_popularity,
CASE
WHEN ( IF(( wc.id_wc > 0 ), ( wc.wc_api_status = 1
AND wc.wc_type = 0
AND wc.class_date > '2010-06-01 22:00:56'
AND wccp.status = 1
AND ( wccp.country_code = 'IE'
OR wccp.country_code IN ( 'INT' )
) ), 0)
) THEN 1
ELSE 0
END AS 'classes_published'
,
CASE
WHEN ( IF(( lp.id_lp > 0 ), ( lp.id_status = 1
AND lp.published = 1
AND lpcp.status = 1
AND ( lpcp.country_code = 'IE'
OR lpcp.country_code IN ( 'INT' )
) ), 0)
) THEN 1
ELSE 0
END AS 'packs_published',
td . *,
u . *
FROM tutor_details AS td
JOIN users AS u
ON u.id_user = td.id_user
LEFT JOIN learning_packs_tag_relations AS lptagrels
ON td.id_tutor = lptagrels.id_tutor
LEFT JOIN learning_packs AS lp
ON lptagrels.id_lp = lp.id_lp
LEFT JOIN learning_packs_categories AS lpc
ON lpc.id_lp_cat = lp.id_lp_cat
LEFT JOIN learning_packs_categories AS lpcp
ON lpcp.id_lp_cat = lpc.id_parent
LEFT JOIN learning_pack_content AS lpct
ON ( lp.id_lp = lpct.id_lp )
LEFT JOIN webclasses_tag_relations AS wtagrels
ON td.id_tutor = wtagrels.id_tutor
LEFT JOIN webclasses AS wc
ON wtagrels.id_wc = wc.id_wc
LEFT JOIN learning_packs_categories AS wcc
ON wcc.id_lp_cat = wc.id_wp_cat
LEFT JOIN learning_packs_categories AS wccp
ON wccp.id_lp_cat = wcc.id_parent
LEFT JOIN order_details AS od
ON td.id_tutor = od.id_author
LEFT JOIN orders AS o
ON od.id_order = o.id_order
LEFT JOIN tutors_tag_relations AS ttagrels
ON td.id_tutor = ttagrels.id_tutor
LEFT JOIN tags AS t
ON t.id_tag = ttagrels.id_tag
LEFT JOIN tags AS tt
ON tt.id_tag = lptagrels.id_tag
LEFT JOIN tags AS ttt
ON ttt.id_tag = wtagrels.id_tag
WHERE ( u.country = 'IE'
OR u.country IN ( 'INT' ) )
AND CASE
WHEN ( ( tt.id_tag = lptagrels.id_tag )
AND ( lp.id_lp > 0 ) ) THEN lp.id_status = 1
AND lp.published = 1
AND lpcp.status = 1
AND ( lpcp.country_code = 'IE'
OR lpcp.country_code IN (
'INT'
) )
ELSE 1
END
AND CASE
WHEN ( ( ttt.id_tag = wtagrels.id_tag )
AND ( wc.id_wc > 0 ) ) THEN wc.wc_api_status = 1
AND wc.wc_type = 0
AND
wc.class_date > '2010-06-01 22:00:56'
AND wccp.status = 1
AND ( wccp.country_code = 'IE'
OR wccp.country_code IN (
'INT'
) )
ELSE 1
END
AND CASE
WHEN ( od.id_od > 0 ) THEN od.id_author = td.id_tutor
AND o.order_status = 'paid'
AND CASE
WHEN ( od.id_wc > 0 ) THEN od.can_attend_class = 1
ELSE 1
END
ELSE 1
END
AND ( t.tag LIKE "%Dictatorship%"
OR t.tag LIKE "%democracy%"
OR tt.tag LIKE "%Dictatorship%"
OR tt.tag LIKE "%democracy%"
OR ttt.tag LIKE "%Dictatorship%"
OR ttt.tag LIKE "%democracy%" )
GROUP BY td.id_tutor
HAVING key_1_total_matches = 1
AND key_2_total_matches = 1
ORDER BY tutor_popularity DESC,
u.surname ASC,
u.name ASC
LIMIT 0, 20
The problem
The results returned by the above query are correct (AND logic working as per expectation), but the time taken by the query rises alarmingly for heavier data and for the current data I have it is like 10 seconds as against normal query timings of the order of 0.005 - 0.0002 seconds, which makes it totally unusable.
Somebody suggested in my previous question to do the following:-
create a temporary table and insert here all relevant data that might end up in the final result set
run several updates on this table, joining the required tables one at a time instead of all of them at the same time
finally perform a query on this temporary table to extract the end result
All this was done in a stored procedure, the end result has passed unit tests, and is blazing fast.
I have never worked with temporary tables till now. Only if I could get some hints, kind of schematic representations so that I can start with...
Is there something faulty with the query?
What can be the reason behind 10+ seconds of execution time?
How tags work in this system?
When a tutor registers, tags are entered and tag relations are created with respect to tutor's details like name, surname etc.
When a Tutors create packs, again tags are entered and tag relations are created with respect to pack's details like pack name, description etc.
tag relations for tutors stored in tutors_tag_relations and those for packs stored in learning_packs_tag_relations. All individual tags are stored in tags table.
Temporary tables are not a silver bullet. The fundamental problem with your queries lies with patterns like this:
t.tag LIKE "%Dictatorship%"
OR tt.tag LIKE "%Dictatorship%"
OR ttt.tag LIKE "%Dictatorship%"
Wildcarding the left side of a LIKE comparison guarantees that an index can not be used. Effectively, you're table scanning all three tables involved...
You need to leverage Full Text Searching, either MySQL's native FTS or 3rd party stuff like Sphinx. All the FTS I've known include a scoring/rank value indicating the strength of the match - you can read the MySQL documentation for the algorithm details. But the score/rank is not the same as what you've got: SUM(DISTINCT LIKE...), you could get the same using something like:
SELECT t.id_tag,
COUNT(*) AS num_matches
FROM TABGS
WHERE MATCH(tag) AGAINST ('Dictatorship')
GROUP BY t.id_tag