Distinct Popular Hits - mysql

Can someone help me with this query:
SELECT su.name,su.imdb , su.id, su.owner, COUNT(*) AS count
FROM subhits AS h LEFT JOIN subtitles AS su ON h.subid=su.id
WHERE su.added BETWEEN '" . $lweek . "' AND '" . $today . "'
GROUP BY h.subid
ORDER BY count DESC LIMIT 15
RETURNS:
name imdb id owner count
*Angels and Demons WTV /title/tt0808151/ 3337 3055 120
Terminator Salvation 2009 /title/tt0438488/ 3539 26 120
Friday The 13th-WTV /title/tt0758746/ 3334 26 82
Night at the Museum /title/tt1078912/ 3548 7170 75
*Angels and Demons WTV2 /title/tt0808151/ 3512 12 10
*DUPLICATED IMDB
As you see it returns results with the same imdb.
What i would like to do is get the highest count, but exclude the lowest imdb count from the result.

What I like about MySQL compared to Oracle, is how other fields not part of GROUP BY can be selected without using grouping functions. What the query below does is take your query and groups it by imdb, which selects a unique imdb with the highest count (which would appear first in the descending sort in the subselect). Try it and see if it works for you.
SELECT name, imdb, id, owner, count
FROM
(
SELECT su.name,su.imdb , su.id, su.owner, COUNT(*) AS count
FROM subhits AS h LEFT JOIN subtitles AS su ON h.subid=su.id
WHERE su.added BETWEEN '" . $lweek . "' AND '" . $today . "'
GROUP BY h.subid
ORDER BY count DESC
)
GROUP BY imdb
LIMIT 15

This may help.
http://www.artfulsoftware.com/infotree/queries.php?&bw=1280#101

I hate to say this, but this is one of those rare occasions where a temp table might help. If you put the results of this:
select
imdb,
id,
count(*) as cnt
from subtitles
group by
imdb,
id
..into a temp table, you can then get the rows you really want:
select
t.imdb,
t.id,
t.cnt
from MyTempTable t
inner join
(
select
imdb,
max(cnt) as maxcnt
from MyTempTable
group by
imdb
) as drv on t.imdb = drv.imdb and t.cnt = drv.maxcnt
..and the results of the above can be joined back into your main table without having to be grouped again.
Does this help?
Apologies for not putting all the SQL in but I'm pressed for time. This will do what you want but will still duplicate if the counts are the same. The query could be modified to deal with this if you care about it.
On a separate note, I'm not sure why you are left joining in the statement when you are not showing any data from the left hand table?

Try this:
SELECT su.name
, su.imdb
, su.id
, su.owner
, (SELECT COUNT(*) FROM dbo.subhits AS h WHERE h.subid = su.id) AS count
FROM dbo.subtitles AS su
ORDER BY count DESC LIMIT 15
Edit: Using the additional information provided I had an almost working solution but got stuck. Combining Chris Simpson's answer with the partial solution I came up with this. It avoids temp tables and has the missing pieces that the other Chris didn't have time to complete. I'm building this in MSSQL so it might need tweaking to work in MySQL. I also can't guarantee the information will be correct without your data but this might help.
SELECT a.id
, (SELECT su.name FROM dbo.subtitles su WHERE a.id = su.id LIMIT 1)
, a.imdb
, (SELECT su.owner FROM dbo.subtitles su WHERE a.id = su.id LIMIT 1)
, a.count
FROM
(
SELECT su.id
, su.imdb
, COUNT(*) AS count
FROM dbo.subtitles su
INNER JOIN dbo.subhits sh ON su.id = sh.subid
GROUP BY su.id, su.imdb
) a
INNER JOIN
(
SELECT c.imdb
, MAX(c.count) AS maxCount
FROM
(
SELECT su.id
, su.imdb
, COUNT(*) AS count
FROM dbo.subtitles su
INNER JOIN dbo.subhits sh ON su.id = sh.subid
GROUP BY su.id, su.imdb
) c
GROUP BY c.imdb
) b ON a.imdb = b.imdb AND a.count = b.maxCount
ORDER BY a.count DESC LIMIT 15

I'd try to test this myself, but I don't have your data or a good sample to test against.
SELECT su.name,su.imdb , su.id, su.owner, COUNT(*) AS counter
FROM subhits AS h LEFT JOIN subtitles AS su ON h.subid=su.id
WHERE su.added BETWEEN '" . $lweek . "' AND '" . $today . "'
GROUP BY h.subid
having max(counter)
ORDER BY count DESC LIMIT 15

Related

SQL query that limits the results to one when using count inside count

I am trying to select the count of likes on a specific project. The idea i came up with is
CAST(count(uploads.ID in (SELECT uploadID from votes)) as decimal) as numberoflikes
this works but the query then only returns one thing.
Entire query
SELECT DISTINCT users.NAME AS username
,users.ID AS userID
,subjects.NAME AS subjectname
,uploads.TIME
,uploads.description
,uploads.NAME
,uploads.ID
,CASE
WHEN uploads.ID IN (
SELECT uploadID
FROM votes
WHERE userID = 2
)
THEN CAST(1 AS DECIMAL)
ELSE CAST(0 AS DECIMAL)
END AS liked
,CASE
WHEN uploads.ID IN (
SELECT uploadID
FROM bookmarks
WHERE userID = 2
)
THEN CAST(1 AS DECIMAL)
ELSE CAST(0 AS DECIMAL)
END AS bookmarked
,CAST(count(uploads.ID IN (
SELECT uploadID
FROM votes
)) AS DECIMAL) AS numberoflikes
FROM uploads
INNER JOIN subjects ON (subjects.ID = uploads.subjectID)
INNER JOIN users ON (users.ID = uploads.userID)
INNER JOIN uploadGrades ON (uploads.ID = uploadGrades.uploadID)
INNER JOIN grades ON (grades.ID = uploadGrades.gradeID)
WHERE uploads.active = 1
AND subjects.ID IN (
SELECT subjectID
FROM userSubjects
INNER JOIN users ON (users.ID = userSubjects.userID)
WHERE userSubjects.userID = 2
)
AND grades.ID IN (
SELECT userGrades.gradeID
FROM uploadGrades
INNER JOIN userGrades ON (uploadGrades.gradeID = userGrades.gradeID)
WHERE userGrades.userID = 2
)
ORDER BY uploads.trueRating DESC;
Lets try a reduce version of your query, That is the base to get better answers
I reduce the initial query to user and upload to start. Also remove the fields you already know how to calculate.
.
SELECT DISTINCT users.NAME AS username
,users.ID AS userID
,uploads.NAME
,uploads.ID
,CAST(count(uploads.ID IN (
SELECT uploadID
FROM votes
)) AS DECIMAL) AS numberoflikes
FROM uploads
INNER JOIN users ON (users.ID = uploads.userID)
WHERE uploads.active = 1
ORDER BY uploads.trueRating DESC;
Then add votes with LEFT JOIN to replace the SELECT in the COUNT that way if not match you will get NULL and as I say in my comment COUNT doesnt count NULL's
.
SELECT DISTINCT users.NAME AS username
,users.ID AS userID
,uploads.NAME
,uploads.ID
,CAST(count(votes.uploadID)) AS DECIMAL) AS numberoflikes
FROM uploads
INNER JOIN users ON (users.ID = uploads.userID)
LEFT JOIN votes ON (uploads.ID = votes.uploadID)
WHERE uploads.active = 1
ORDER BY uploads.trueRating DESC;
Try something like this...
SELECT users.name as username, users.ID as userID, subjects.name as subjectname,
uploads.time, uploads.description, uploads.name, uploads.ID,
count(userVotes.userId), count(bookmarksMade.userId),
FROM uploads
join subjects on(subjects.ID = uploads.subjectID)
join users on(users.ID = uploads.userID)
join uploadGrades on(uploads.ID = uploadGrades.uploadID)
join grades on(grades.ID = uploadGrades.gradeID)
left join (select userId, uploadId from votes where userId = 2) as userVotes on uploads.id = userVotes.uploadId
left join (select userId, uploadId from bookmarks where userId = 2) as bookmarksMade on uploads.id = bookmarksMade.uploadId
join userSubjects on subjects.id = userSubjects.subjectID
WHERE uploads.active = 1 AND
userSubjects.userID = 2
ORDER BY uploads.trueRating DESC;
But, I am leaving out the userGrades thing, because you are doing a funky join there that I don't really understand (joining two tables on something that looks like it is not the whole primary key on either table).
Anyway, you really need to go to something more like this or what Oropeza suggests in his answer. Get more direct about what you want. This query looks like a monster that has been growing and getting things added in with "IN" clauses, as you needed them. Time to go back to the drawing board and think about what you want and how to get at it directly.
count(uploads.ID in (SELECT uploadID from votes)) as numberoflikes
group by uploads.Id ORDER BY uploads.trueRating DESC
I managed to do it like this. If i added the group by then it split the numberoflikes into rows and returned more then one row. Thanks for the help!

SELECT only first and last results

A client can have more than one equipment (SerialNo). Each equipment has a cost and every month there is data recorded for each equipment. I'm trying to select only the first and last result for each equipment based on the queried period.
"
SELECT i.SerialNo
, p.Name
, c.Cost
, ci.DataDate
, ci.Data
,
FROM install i
JOIN product p USING (ProductId)
JOIN counter c USING (InstallId)
JOIN counter_item ci USING (CounterId)
WHERE i.ClientId LIKE $clientId
AND MONTH(ci.DataDate) BETWEEN $mStart AND $mEnd
";
This select works but it retrieves all records between the starting date and finishing date.
I tried, to get the top results and figured I would use A UNION ALL to combine with the bottom results (ci.DataDate ASC), but it's not working. I only get the first record encounter.
GROUP BY i.SerialNo
ORDER BY ci.DataDate DESC
It's like ORDER BY has no effect at all.
In counter_item you find the first and last DataDate per CounterId for the time range. So find these first by aggregation and use this information in order to join the desired records:
SELECT i.SerialNo,
p.Name,
c.Cost,
ci.DataDate,
ci.Data
FROM install i
JOIN product p ON p.ProductId = i.ProductId
JOIN counter c ON c.InstallId = i.InstallId
JOIN
(
SELECT CounterId, MIN(DataDate) AS MinDate, MAX(DataDate) AS MaxDate
FROM counter_item
WHERE MONTH(DataDate) BETWEEN $mStart AND $mEnd
GROUP BY CounterId
) minmax ON minmax.CounterId = c.CounterId
JOIN counter_item ci ON ci.CounterId = minmax.CounterId
AND ci.DataDate IN (minmax.MinDate, minmax.MaxDate)
WHERE i.ClientId LIKE $clientId
ORDER BY i.SerialNo, ci.DataDate
You could do it in next way, here is just general idea of how that could be done:
select * from table
where
([row] = (select max([row]) from table ) or
[Date] = (select min([row]) from table ))
You may also be able to use a cross apply. Something like this, untested rough sample:
SELECT i.SerialNo,
p.Name,
c.Cost,
MIN(ci.DataDate) as MinDate,
b.MaxDate,
ci.Data,
FROM install i
CROSS APPLY (SELECT
MAX(ci.DataDate) as MaxDate
FROM install
JOIN counter_item ci USING (CounterId)
WHERE i.ClientId LIKE $clientId
AND MONTH(ci.DataDate) BETWEEN $mStart AND $mEnd) b
WHERE i.ClientId LIKE $clientId
AND MONTH(ci.DataDate) BETWEEN $mStart AND $mEnd
GROUP BY i.SerialNo
ORDER BY ci.DataDate DESC

Got Sql Error in Syntax and need efficient sql query

As per my requirement i made the below query. Now it not working.
Query is:
SELECT *
FROM T_INV_DTL T
LEFT JOIN (
SELECT inv_dtl_id,
Employee_id AS emp_id,
GROUP_CONCAT(DISTINCT Employee_id) AS Employee_id
FROM T_INV_INVESTIGATOR
GROUP BY
inv_dtl_id
)TII
ON T.inv_dtl_id = TII.inv_dtl_id
JOIN T_INVESTIGATION TI
ON T.inv_id = TI.inv_id
LEFT JOIN (
SELECT inv_dtl_id
FROM T_INV_BILL
GROUP BY
inv_dtl_id
)TIB
ON T.inv_dtl_id = TIB.inv_dtl_id
JOIN T_Insurance_company TIC
ON TI.client_id = TIC.ins_cmp_id
WHERE 1 T.Report_dt != '0000-00-00'
AND (
T.inv_dtl_id NOT IN (SELECT inv_dtl_id
FROM T_INV_BILL TIBS
WHERE TIBS.inv_dtl_id NOT IN (SELECT
inv_dtl_id
FROM
T_INV_BILL
WHERE
Bill_submitted_dt =
'0000-00-00'))
)
ORDER BY
Allotment_dt DESC
LIMIT 20
Can anyone tells the problem and can you please modify to more efficient query(Suppose if we have more than 100 records, then we take the count for it for pagination it should be give faster).
T_INV_DTL is main table and it connect to others. So my probelm is each entry of this table T_INV_DTL has multtiple investigation bill in the table T_INV_BILL. Report_dt in the T_INV_DTL. So my outcome is that i need result if there’s a report date in T_INV_DTL and not atleast one bill date in T_INV_BILL.
I need the result with both if there’s a report date in T_INV_DTL and not atleast one bill date in T_INV_BILL(If all have entered the bill submitted date it does not need it).
While I admittedly don't know what issues you're having (please provide addl info), your query does look like it could be optimized.
Removing your Where criteria and adding to your Join should save 2 of your table scans:
SELECT *
FROM T_INV_DTL T
LEFT JOIN (
SELECT inv_dtl_id,
Employee_id AS emp_id,
GROUP_CONCAT(DISTINCT Employee_id) AS Employee_id
FROM T_INV_INVESTIGATOR
GROUP BY
inv_dtl_id
)TII
ON T.inv_dtl_id = TII.inv_dtl_id
JOIN T_INVESTIGATION TI
ON T.inv_id = TI.inv_id
LEFT JOIN (
SELECT inv_dtl_id
FROM T_INV_BILL
WHERE Bill_submitted_dt != '0000-00-00'
GROUP BY inv_dtl_id
)TIB
ON T.inv_dtl_id = TIB.inv_dtl_id
JOIN T_Insurance_company TIC
ON TI.client_id = TIC.ins_cmp_id
WHERE T.Report_dt != '0000-00-00'
AND TIB.inv_dtl_id IS NULL
ORDER BY
Allotment_dt DESC
LIMIT 20

Group by subkey but if new read if not 1 show 0

Ok I know this is going to sound stupid. But I have tried everything.
Here is my code to start of with
SELECT toD.username AS ToUser,
fromD.username AS FromUser,
rvw.* FROM usermessages AS rvw
LEFT JOIN users AS toD
ON toD.id = rvw.touserid
LEFT JOIN users AS fromD ON fromD.id = rvw.fromuserid
WHERE touserid = '" . $this->userid . "'
AND deleted = '0'
GROUP BY subkey
ORDER BY rvw.read ASC, rvw.created DESC
while this does work, what I am finding is that if there is a new message, and the read is 0 it still shows up as 1. I know this is because I am grouping the rows together.
But am not sure of any other way to do this.
It doesn't work because mysql can return any row from the group no matter how you try to order your set. To find first row in the group using some custom order you have to split it into two tasks - first finding all distinct values for the column you group by and then finding first row in the subquery for every referenced value. So your query should look like:
SELECT toD.username AS ToUser, fromD.username as FromUser, msg.* FROM
( SELECT DISTINCT touserid, subkey
FROM usermessages
WHERE touserid = 'insert_your_id_here'
AND deleted=0 ) msgg
JOIN usermessages msg
ON msg.id = ( SELECT msgu.id
FROM usermessages msgu
WHERE msgu.touserid = msgg.touserid
AND msgu.subkey = msgg.subkey
AND deleted=0
ORDER BY msgu.read ASC, msgu.created DESC
LIMIT 1 )
JOIN users fromD ON msg.fromuserid = fromD.id
JOIN users toD ON msg.touserid = toD.id
Make sure you have an index on (touserid,subkey). Depending on how big your db is you may need more.

mysql select where count = 0

In my db, I have a "sales" table and a "sales_item". Sometimes, something goes wrong and the sale is recorded but not the sales item's.
So I'm trying to get the salesID from my table "sales" that haven't got any rows in the sales_item table.
Here's the mysql query I thought would work, but it doesn't:
SELECT s.*
FROM sales s NATURAL JOIN sales_item si
WHERE s.date like '" . ((isset($_GET['date'])) ? $_GET['date'] : date("Y-m-d")) . "%'
AND s.sales_id like '" . ((isset($_GET['shop'])) ? $_GET['shop'] : substr($_COOKIE['shop'], 0, 3)) ."%'
HAVING count(si.sales_item_id) = 0;
Any thoughts?
Where does the table alias v does come from?
Showing the table definition would be a really good idea ;)
It has to be a left join:
SELECT *
FROM table1
LEFT JOIN table2 ON(table1.id = table2.table1_id)
WHERE table2.table1_id IS NULL
Table1 is your sales-Table and table2 is you sales_item
Having always used with Group By
GROUP BY si.sales_item_id
HAVING count(si.sales_item_id) = 0;
You don't need to join the tables, you can use something like:
[...] WHERE sales.id NOT IN (SELECT sales_id FROM sales_item)
This filters only the sales that do not have any corresponding sales_item entries.
For completeness ....
SELECT S.*
FROM SALES S
WHERE NOT EXISTS (
SELECT 1
FROM SALES_ITEM SI
WHERE SI.SALES_ITEM_ID = S.ID)
MySQL can have issues with IN clauses.
Assuming that each item in sales_item has an associated sales_id against it, you are probably looking for all sales that have no items.
How about using a subquery? Get all the sales_ids from the sales table where the id does not exist in the items table...
SELECT * from sales where sales_id not in (SELECT DISTINCT sales_id from sales_item)
(note : Exact syntax may be wrong, but the idea should be sound, if I understood the question correctly)
The join is restricting the rows to be displayed.
My advice is to forget about the join and use instead something like this:
select *
from sales
where salesId not in (select salesId from sales_item)
Basically, returns sales that doesn't have any associated sales_item.
Good luck
You should probably group rows by sales item id.
SELECT s.id, count(*) as no_of_items
FROM sales s NATURAL JOIN sales_item si
WHERE s.date like '" . ((isset($_GET['date'])) ? $_GET['date'] : date("Y-m-d")) . "%'
AND v.sales_id like '" . ((isset($_GET['shop'])) ? $_GET['shop'] : substr($_COOKIE['shop'], 0, 3)) ."%'
GROUP BY si.salesitem_id
HAVING no_of_items = 0;