arel union and latest conversation from messages - mysql

I need a list of messages where each one is the most recent in the "conversation" between the current user and each other user.
The same query is described in this question
The code I have so far is:
t1 = Arel::Table.new(:messages, :as => 't1')
t2 = Arel::Table.new(:messages, :as => 't2')
convs1 = t1.
project(
t1[:receiver_user_id].as('other_user_id'),
t1[:receiver_user_id].as('receiver_user_id'),
t1[:sender_user_id].as('sender_user_id'),
t1[:created_at].as('created_at')
).
where(t1[:sender_user_id].eq(user.id))
convs2 = t2.project(
t2[:sender_user_id].as('other_user_id'),
t2[:receiver_user_id].as('receiver_user_id'),
t2[:sender_user_id].as('sender_user_id'),
t2[:created_at].as('created_at')
).
where(t2[:receiver_user_id].eq(user.id))
conv = convs1.union(convs2)
First off, I get an error:
ActiveRecord::StatementInvalid: Mysql2::Error: You have an error in your SQL syntax; check \
the manual that corresponds to your MySQL server version for the right syntax to use near \
'UNION SELECT `t2`...
This works if I manually replace "UNION" with "UNION ALL" in the sql produced below.
conv.to_sql from the above code produces:
SELECT `t1`.`receiver_user_id` AS other_user_id,
`t1`.`receiver_user_id` AS receiver_user_id, `
t1`.`sender_user_id` AS sender_user_id,
`t1`.`created_at` AS created_at
FROM `messages` `t1`
WHERE `t1`.`sender_user_id` = 50
UNION
SELECT `t2`.`sender_user_id` AS other_user_id,
`t2`.`receiver_user_id` AS receiver_user_id,
`t2`.`sender_user_id` AS sender_user_id,
`t2`.`created_at` AS created_at
FROM `messages` `t2`
WHERE `t2`.`receiver_user_id` = 50
Any idea why there's a MySQL UNION error. Is it an arel bug?
Secondly, any help with completing the query would be much appreciated.
Update:
Using Arel::Nodes::Union.new works

I think this is more probably a mysql fault, this is a mySQL error text. Something similar is discussed in here, but not exactly this issue.
Try to migrate to another sql server, and check again, or if union all works then use this:
conv = convs1.union(convs2, :all)
Based on documentation.

The problem is actually the parentheses in the sql. It works if I run:
Message.find_by_sql conv.to_sql.delete('()')
which removes the leading "(" and trailing ")"
Weird.. I don't know how I would chain this to complete the query. (Arel::Nodes::Union doesn't have a group method). This is Rails 3.1.4

I had a similar problem and solved it as follows:
def last_messages
Message.find_by_sql("
SELECT messages.*,
(IF(recipient_id = #{id}, 0,1)) as outlast,
users.avatar_name,
users.name
FROM messages
INNER JOIN users
ON users.id=(IF(recipient_id = #{id}, sender_id,recipient_id))
WHERE messages.id IN
( SELECT max(id)
FROM messages
WHERE recipient_id = #{id} OR sender_id = #{id}
GROUP BY (IF(recipient_id = #{id}, sender_id, recipient_id))
)
ORDER BY messages.id DESC")
end

This is the code I used instead in the end
all_msgs = Message.where("messages.sender_user_id = ? OR messages.receiver_user_id = ?",
user.id, user.id)
msg_ids = all_msgs.select("sender_user_id, receiver_user_id, max(id) as max_id")
.group(:sender_user_id, :receiver_user_id).map { |m| m.max_id }
all_msgs = all_msgs.where(:id => msg_ids)

Related

SQL nested queries and subqueries

i've been having an error with this query and i'm not sure how to fix it. this query is supposed to filter occupations stored in my database to match the volunteer's occupation. please help me fix my query. all the names in this query are correctly spelled, i double checked all the spellings before writing the query here.
the error says "#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'SELECT (SELECT count(*) FROM occupation_event WHERE event_id='8' AND occupationN' at line 1"
SELECT
*
FROM
volunteer_details
WHERE
user_id=73
AND
volunteer_occupation in (
SELECT
occupationName
FROM
occupation_event
WHERE
event_id=8
OR SELECT (
SELECT
count(*)
FROM
occupation_event
WHERE
event_id='8'
AND
occupationName = 'No Occupation Required') > 0 AS need
)
I think the error is the as need at the end. I would write this as:
SELECT vd.*
FROM volunteer_details vd
WHERE user_id = 73 AND
(vd.volunteer_occupation in (SELECT oe.occupationName FROM occupation_event oe WHERE oe event_id = 8) or
exists (select 1 from occupation_event oe where event_id = 8 and oe.occupationName = 'No Occupation Required')
);

How can I get the latest package for all packages in SQL?

I have two tables, packages (with id, name as attributes) and releases (with url, upload_time, downloaded_bytes as attributes). Every package can have arbitrary many releases. I want a list of all packages with their latest release.
Currently, I have the following working code:
sql = ("SELECT `packages`.`id`, `name` FROM `packages`")
cursor.execute(sql)
packages = cursor.fetchall()
for pkg in packages:
sql = ("SELECT `url` FROM `releases` "
"WHERE `package_id` = %s "
"AND `downloaded_bytes` = 0 "
"ORDER BY `upload_time` DESC LIMIT 1")
cursor.execute(sql, (pkg['id'], ))
url = cursor.fetchone()
if url is not None:
package_url = url['url']
package_analysis.main(pkg['name'], package_url)
logging.info("Package '%s' done.", pkg['name'])
However, I think this is an ugly solution as I execute a lot of queries where I should only execute one query.
Can I do this in one query? How would the query look like?
Please note: I only want one result for each package. That means, the package numpy should only give the result for url="https://pypi.python.org/packages/cp35/n/numpy/numpy-1.10.1-cp35-cp35m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl" (version 1.10.1) and not 99 results.
What I've tried
SELECT
`packages`.`id`,
`packages`.`name`,
`releases`.`url`,
`releases`.`upload_time`,
`releases`.`release_number`
FROM
`packages`
JOIN
`releases` ON `releases`.`package_id` = `packages`.`id`
GROUP BY
`packages`.`name`
ORDER BY
`releases`.`upload_time` DESC
But that gives a seemingly random value for upload_time (and also url).
You can try this query:
select p.id, p.name, r.url, r.upload_time, r.release_number from
(select p.id, max(r.release_number) release_number from packages p
join releases r on p.id = r.package_id
group by p.id) a
join packages p on p.id = a.id
join releases r on r.release_number = a.release_number
It assumes that release_number is sortable, if not possible you can use max upload time instead.
Based on this answer (thank you Emiswelt) for mentioning it:
SELECT
`packages`.`id`,
`packages`.`name`,
`o`.`url`,
`o`.`upload_time`,
`o`.`release_number`
FROM
`releases` o
LEFT JOIN
`releases` b ON `o`.`package_id` = `b`.`package_id`
AND `o`.`upload_time` < `b`.`upload_time`
JOIN
`packages` ON `packages`.`id` = o.package_id
WHERE
`b`.`upload_time` is NULL
AND `o`.`downloaded_bytes` = 0
ORDER BY
`packages`.`name`
LIMIT 10
The query finishes execution within a fraction of a second.

SQL Error : USE `most`; SELECT `item_name`, SUM(`stock_invent`) AS `Total Items` FROM inventories GROUP BY `item_name

I was trying to print out the list total of items by SQL. It works well on phpmyadmin but not in Dreamweaver. I dont know what is wrong with my SQL. Anyone can help me?
mysql_select_db($database_dbcon, $dbcon);
$query_Recordset1 = "USE `most`; SELECT `item_name`, SUM(`stock_invent`) AS `Total Items` FROM inventories GROUP BY `item_name`";
$Recordset1 = mysql_query($query_Recordset1, $dbcon) or die(mysql_error());
$row_Recordset1 = mysql_fetch_assoc($Recordset1);
$totalRows_Recordset1 = mysql_num_rows($Recordset1);
i got this kind of messages :
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near 'SELECT item_name, SUM(stock_invent) AS Total Items FROM
inventories GROUP ' at line 1
The use command is used in phpMyAdmin (as well as in mysql's command line tool) to select the database you want to run queries against. In PHP, you should not need it - that's what mysql_select_db is for:
mysql_select_db('most', $dbcon);
$query_Recordset1 = "SELECT `item_name`, SUM(`stock_invent`) AS `Total Items` FROM inventories GROUP BY `item_name`";
mysql_select_db('most', $dbcon);
$query_Recordset1 = "SELECT item_name, SUM(stock_invent) AS Total Items FROM inventories GROUP BY item_name ";
$Recordset1 = mysql_query($query_Recordset1, $dbcon) or die(mysql_error());
$row_Recordset1 = mysql_fetch_assoc($Recordset1);
$totalRows_Recordset1 = mysql_num_rows($Recordset1);

How to access subquery table in the main query with MySQL?

select m1.id, m1.status, at.view_data, at.view_graph, ta.tag_string
from
access_tbl at, image_campaign_tbl m1
RIGHT JOIN
(select
GROUP_CONCAT(t.name) as tag_string , c.image_campaign_id
from campaign_tags_tbl c,tag_tbl t
where c.tag_id=t.id
$tag_q
group by c.image_campaign_id
) as ta
ON ta.image_campaign_id=m1.id
where
m1.client_id =$client_id
and m1.client_id = at.client_id
$prev_filter
limit $start,$end;
Error message:
in LOGS: DBD::mysql::db selectall_arrayref failed: Unknown column 't.name' in 'where clause' at /home/sakthi/rtads/Project/pm/Image/UI.pm line 2536.**
In Perl Module, I'm passing the same value of $tag_q to the $prev_filter to get the Pagination of filter based on TAGS values in the next page
if ( $prev_filter eq '' ) {
$prev_filter =
$search_clist_q . ' '
. $tag_q . ' '
}
From the error msg, I got the error which I'm doing. Since I'm trying to access the table of subquery in the main query, this error is happening.
So I want to know how to access the tag_string(or)t.name outside the subquery.
First of all, i suggest you to avoid use of old school syntax for jointures (FROM table1, table2,... WHERE table1.column1 = table2.column2 AND ...).
Here is the query that seems to return what you're looking for:
SELECT IC.id
,IC.status
,A.view_data
,A.view_graph
,TA.tag_string
FROM access_tbl A
INNER JOIN image_campaign_tbl IC ON IC.client_id = A.client_id
AND IC.client_id = $client_id
RIGHT JOIN (SELECT CT.image_campaign_id
,GROUP_CONCAT(T.name) AS [tag_string]
FROM campaign_tags_tbl CT
INNER JOIN tag_tbl T ON T.id = CT.tag_id
GROUP BY CT.image_campaign_id) TA ON TA.image_campaign_id = IC.id
WHERE <Your filters here>
LIMIT $start, $end
Hope this will help you.

Getting the closest time in a PDO statement

I am working from 2 databases and I need to find records which matches the closest times. Both fields are datetime().
So in essence:
table1.time = 2012-06-07 15:30:00
table2.time = 2012-06-07 15:30:01
table2.time = 2012-06-07 15:30:02
table2.time = 2012-06-07 15:30:03
NOTE: The table I am querying (table2) is a mssql table, and table1.time is a datetime() time. I need to find in table2 the row which closest matches table1.time, but I have no guarnatee that it would be an exact match, so I need the closest. I only need to return 1 result.
I tried the SQL below based on an example from a previous stackoverflow query but it failed to work.
Table1 is a mysql database where table2 is mssql and the query happens on table2 (mssql)
try {
$sql = "
SELECT
PCO_AGENT.NAME,
PCO_INBOUNDLOG.LOGIN AS LOGINID,
PCO_INBOUNDLOG.PHONE AS CALLERID,
PCO_INBOUNDLOG.STATION AS EXTEN,
PCO_INBOUNDLOG.TALKTIME AS CALLLENGTH,
PCO_INBOUNDLOG.CHANNELRECORDID AS RECORDINGID,
PCO_SOFTPHONECALLLOG.RDATE,
PCO_INBOUNDLOG.RDATE AS INBOUNDDATE
FROM
PCO_INBOUNDLOG
INNER JOIN
PCO_LOGINAGENT ON PCO_INBOUNDLOG.LOGIN = PCO_LOGINAGENT.LOGIN
INNER JOIN
PCO_SOFTPHONECALLLOG ON PCO_INBOUNDLOG.ID = PCO_SOFTPHONECALLLOG.CONTACTID
INNER JOIN
PCO_AGENT ON PCO_LOGINAGENT.AGENTID = PCO_AGENT.ID
WHERE
PCO_INBOUNDLOG.STATION = :extension
AND ABS(DATEDIFF(:start,PCO_SOFTPHONECALLLOG.RDATE))
";
$arr = array(":extension" => $array['extension'], ":start" => $array['start']);
$query = $this->mssql->prepare($sql);
$query->execute($arr);
$row = $query->fetchAll(PDO::FETCH_ASSOC);
$this->pre($row);
}
I am getting the following error at the moment:
SQLSTATE[HY000]: General error: 174 General SQL Server error: Check messages from the SQL Server [174] (severity 15) [(null)]SQLSTATE[HY000]: General error: 174 General SQL Server error: Check messages from the SQL Server [174] (severity 15) [(null)]
Found a shorter version:
SELECT * FROM `table` WHERE `date` < '$var' ORDER BY date LIMIT 1;