select multiple tables in single sql query - sql

The other threads about this didn't seem to help me. I want to select all the information from one table, but order them by a value in another table.
SELECT message,
DATE,
ip,
name,
website,
id
FROM guestbook_message
WHERE deleted = 0
AND DATE > Date_sub(Now(), interval 1 day)
ORDER BY DATE DESC
Except I need to ORDER BY 'votes' DESC; which is in another table called m_votes.
Is it possible to do this? I have read on another website that this query is impossible.
$query="SELECT g.message,
g.DATE,
g.ip,
g.name,
g.website,
g.id
FROM guestbook_message AS g
join m_votes AS v
ON g.id = v.vid
WHERE g.deleted = 0
AND v.messageid = $mid
AND g.DATE > Date_sub(Now(), interval 1 day)
ORDER BY SUM(v.votes) DESC;"
^^This doesn't work

You need a join:
SELECT
g.message,
g.date,
g.ip,
g.name,
g.website,
g.id
FROM guestbook_message AS g
LEFT JOIN m_votes AS v
ON g.id = v.message_id
WHERE g.deleted = 0
AND g.date > NOW() - INTERVAL 1 DAY
GROUP BY g.id
ORDER BY COUNT(v.message_id) DESC

I'm a bit of a beginner and to me this is a very hard query as it needs to find the SUM of 'votes' in m_votes and order by that. As well as get the information from the other query.
This is the query that gets the information about the message:
"SELECT message,
`date`,
ip,
name,
website,
id
FROM `guestbook_message`
WHERE deleted = 0
AND date > DATE_SUB(NOW(), INTERVAL 1 DAY)
ORDER BY `date` DESC";
And this is the query that gets the information about the votes:
"SELECT SUM(votes) as votes FROM m_votes WHERE messageid = $mid"
But I have no idea who I would put them into one query that will gather all the information from the first query, then ORDER them by votes.

You have to join the data, that is, you need to have the votes for each guestbook message.
Let's suppose for simplicity you have the following tables:
Message
----
id INT
messsageText VARCHAR(5000)
and
MessageVotes
------------
messageId INT (references the `id` column in table Message)
voteValue INT (suppose it can be +1 or -1, whatever)
votingIp VARCHAR(100)
Then you could do something like
SELECT
m.id,
m.messageText,
SUM(mv.voteValue) AS votes
FROM
Message AS m,
MessageVotes AS mv
WHERE
mv.messageId = m.id
GROUP BY
m.id, m.messageText /* here you need to place every field you `select` from Message */
ORDER BY
SUM(mv.voteValue) DESC
or even better:
SELECT
m.id,
m.messageText,
SUM(mv.voteValue) AS votes
FROM
Message AS m
LEFT JOIN MessageVotes AS mv ON mv.messageId = m.id
GROUP BY
m.id, m.messageText
ORDER BY
SUM(mv.voteValue) DESC
See:
mysql joins,
mysql left join
mysql group by - aggregate functions
Join Syntax

Please try below query, you missed group by clause.
SELECT g.message,
g.date,
g.ip,
g.name,
g.website,
g.id
FROM guestbook_message AS g
JOIN m_votes AS v ON g.id = v.vid
WHERE g.deleted = 0
AND v.messageid = $mid
AND g.date > DATE_SUB(NOW(), INTERVAL 1 DAY)
GROUP BY g.message,
g.date,
g.ip,
g.name,
g.website,
g.id
ORDER BY SUM(v.votes) DESC;

Related

How to increment rank when same total marks is there using mysql query?

This is my query:
SELECT
`users`.`id` AS userID,
`user_tests`.`id`,
`users`.`profilePic`,
`users`.`firstName`,
`user_tests`.`userId`,
`user_tests`.`isFirstAttempt`,
`user_tests`.`total_marks`,
FIND_IN_SET(
`user_tests`.`total_marks`,
(
SELECT
GROUP_CONCAT(
DISTINCT `user_tests`.`total_marks`
ORDER BY
CAST(
`user_tests`.`total_marks` AS DECIMAL(5, 3)
)
DESC
)
FROM
`user_tests`
WHERE
`user_tests`.`testSeriesId` = '856' AND `user_tests`.`isFirstAttempt` = '1'
)
) AS rank,
FROM
`user_tests`
LEFT JOIN `users` ON `users`.id = `user_tests`.`userId`
WHERE
`user_tests`.`isFirstAttempt` = '1' AND `user_tests`.`testSeriesId` = '856'
ORDER BY
CAST(
`user_tests`.`total_marks` AS DECIMAL(5, 3)
)
DESC
,
`submissionTimeInMinutes` ASC,
`rank` ASC;
Output:
this is the image
Expected:
Here is the expected output image
I am using MariaDB 5.5.68
I've tried using variables to increment but it's showing me the current row number instead of 1,2,3,4,5... numbers.
Can anybody help here?
Thanks.
You don't appear to need a left join as none of the user names are missing. A simple subquery will give you the the ranking by total_mark but I'm not sure whether you're counting users or something else. A CTE would let you avoid duplicating some of the logic and might possibly even be faster. I don't know if you have that option.
SELECT
u.id AS userID, t.id, u.profilePic, u.firstName,
t.userId, t.isFirstAttempt, t.total_marks,
(
SELECT count(distinct userId) from FROM user_tests t2
WHERE t.isFirstAttempt = 1 AND t.testSeriesId = 856
AND t.total_marks < t2.total_marks
) as num_tests
FROM user_tests t inner join users u ON u.id = t.userId
WHERE t.isFirstAttempt = 1 AND t.testSeriesId = 856
ORDER BY total_marks desc, submissionTimeInMinutes, num_tests desc

How to join table to table created from query

I query to get the top 5 results of IPs and then i want to get for each IP, the countries and others fields related to it by join.
select actual_ip, actual_country_code, actual_country_name, organization FROM "public"."bus_request" inner join (
select top 5 actual_ip, count(*) FROM "public"."bus_request"
where app_name = 'xxxxx' and request_score>0 and date >= '2019-06-07' and event_type <> 'browser_js'
group by actual_ip order by count desc ) as temp on actual_ip = temp.actual_ip
SQL Error [500310] [42702]: [Amazon](500310) Invalid operation: column reference "actual_ip" is ambiguous;
Not sure exactly which DBMS you're using, but try resolving the ambiguity by specifying the table, e.g.:
select busreq.actual_ip, actual_country_code, actual_country_name, organization FROM "public"."bus_request" as busreq inner join (
select top 5 actual_ip, count(*) FROM "public"."bus_request"
where app_name = 'xxxxx' and request_score>0 and date >= '2019-06-07' and event_type <> 'browser_js'
group by actual_ip order by count desc ) as temp on busreq.actual_ip = temp.actual_ip
(you may need to disambiguate the inner use of actual_ip - but hopefully not.)
You should always qualify all column references in a query that references more than one table. In addition, you should use table aliases so the query is easier to write and to read:
select br.actual_ip, br.actual_country_code, br.actual_country_name, br.organization
from "public"."bus_request" br inner join
(select top 5 br2.actual_ip, count(*)
from "public"."bus_request" br2
where br2.app_name = 'xxxxx' and
br2.request_score > 0 and
br2.date >= '2019-06-07' and
br2.event_type <> 'browser_js'
group by br2.actual_ip
order by count(*) desc
) br2
on br2.actual_ip = br.actual_ip;

SQL query to get 10 last topics with replies

I have a simple forum database and want to write SQL query to get
10 last topics (threads) with replies from 3 or more unique posters.
Result: Topic (thread) name | Last message text | User name | Date
UPDATED:
Here is what I tried:
SELECT thread.thread_id, thread.thread_name, message.thread_id,
COUNT(message.thread_id)
FROM thread, message
WHERE message.thread_id = thread.thread_id
GROUP BY message.thread_id
HAVING COUNT(message.thread_id) >= 3
LIMIT 10
But it returns 10 topics (threads) with 3 or more replies (but not 3 or more unique posters)
UPDATE 2:
SELECT message.thread_id AS ID, thread.thread_name AS topic
FROM message INNER JOIN thread
ON message.thread_id = thread.thread_id
GROUP BY message.thread_id
HAVING COUNT(*) >= 3
LIMIT 10
This also returns 10 topics with 3 or more replies
UPDATE 3, thanks #shawnt00
SELECT thread.thread_name AS 'Topic', message_text AS 'Message', person_nickname AS 'Nickname', message_date AS 'Date'
FROM thread,
(
SELECT thread.thread_id, MAX(message_date) AS last_date
FROM thread
INNER JOIN message ON message.thread_id = thread.thread_id
GROUP BY thread.thread_id
HAVING COUNT(DISTINCT message.person_id) >= 3
) AS temp
INNER JOIN message
ON message.thread_id = temp.thread_id AND message.message_date = temp.last_date
INNER JOIN person ON person.person_id = message.person_id
WHERE thread.thread_id = temp.thread_id
ORDER BY message.message_date DESC
LIMIT 10
with data as (
select p.thread_id, max(message_date) as last_date
from thread t inner join message m on m.thread_id = t.thread_id
group by p.thread_id
having count(distinct m.person_id) >= 3
)
select *
from data d
inner join message m
on m.thread_id = d.thread_id and m.message_date = m.last_date
inner join person p on p.person_id = m.person_id;
The only assumption is that there can't be ties on dates. If you have analytic functions available then there are other approaches using those. I'm sure you can figure out how to get top 10.
i think you need below query.
Assuming that you are using mysql so i used Limitfunction as you want last 3 unique row
select distinct th.thread_name as Topic_name, m.message_text, p.person_nickname as User_name, m.message_date as Date from message m
inner join thread th on m.thread_id=th.thread_id
inner join persion p on p.persion_id=m.persion_id
order by message_date desc
Limit 3
But if the db is SQLSERVER Then use below query
select distinct TOP(3) th.thread_name as Topic_name, m.message_text, p.person_nickname as User_name, m.message_date as Date from message m
inner join thread th on m.thread_id=th.thread_id
inner join persion p on p.persion_id=m.persion_id
order by message_date desc

Limit join to one row

I have the following query:
SELECT sum((select count(*) as itemCount) * "SalesOrderItems"."price") as amount, 'rma' as
"creditType", "Clients"."company" as "client", "Clients".id as "ClientId", "Rmas".*
FROM "Rmas" JOIN "EsnsRmas" on("EsnsRmas"."RmaId" = "Rmas"."id")
JOIN "Esns" on ("Esns".id = "EsnsRmas"."EsnId")
JOIN "EsnsSalesOrderItems" on("EsnsSalesOrderItems"."EsnId" = "Esns"."id" )
JOIN "SalesOrderItems" on("SalesOrderItems"."id" = "EsnsSalesOrderItems"."SalesOrderItemId")
JOIN "Clients" on("Clients"."id" = "Rmas"."ClientId" )
WHERE "Rmas"."credited"=false AND "Rmas"."verifyStatus" IS NOT null
GROUP BY "Clients".id, "Rmas".id;
The problem is that the table "EsnsSalesOrderItems" can have the same EsnId in different entries. I want to restrict the query to only pull the last entry in "EsnsSalesOrderItems" that has the same "EsnId".
By "last" entry I mean the following:
The one that appears last in the table "EsnsSalesOrderItems". So for example if "EsnsSalesOrderItems" has two entries with "EsnId" = 6 and "createdAt" = '2012-06-19' and '2012-07-19' respectively it should only give me the entry from '2012-07-19'.
SELECT (count(*) * sum(s."price")) AS amount
, 'rma' AS "creditType"
, c."company" AS "client"
, c.id AS "ClientId"
, r.*
FROM "Rmas" r
JOIN "EsnsRmas" er ON er."RmaId" = r."id"
JOIN "Esns" e ON e.id = er."EsnId"
JOIN (
SELECT DISTINCT ON ("EsnId") *
FROM "EsnsSalesOrderItems"
ORDER BY "EsnId", "createdAt" DESC
) es ON es."EsnId" = e."id"
JOIN "SalesOrderItems" s ON s."id" = es."SalesOrderItemId"
JOIN "Clients" c ON c."id" = r."ClientId"
WHERE r."credited" = FALSE
AND r."verifyStatus" IS NOT NULL
GROUP BY c.id, r.id;
Your query in the question has an illegal aggregate over another aggregate:
sum((select count(*) as itemCount) * "SalesOrderItems"."price") as amount
Simplified and converted to legal syntax:
(count(*) * sum(s."price")) AS amount
But do you really want to multiply with the count per group?
I retrieve the the single row per group in "EsnsSalesOrderItems" with DISTINCT ON. Detailed explanation:
Select first row in each GROUP BY group?
I also added table aliases and formatting to make the query easier to parse for human eyes. If you could avoid camel case you could get rid of all the double quotes clouding the view.
Something like:
join (
select "EsnId",
row_number() over (partition by "EsnId" order by "createdAt" desc) as rn
from "EsnsSalesOrderItems"
) t ON t."EsnId" = "Esns"."id" and rn = 1
this will select the latest "EsnId" from "EsnsSalesOrderItems" based on the column creation_date. As you didn't post the structure of your tables, I had to "invent" a column name. You can use any column that allows you to define an order on the rows that suits you.
But remember the concept of the "last row" is only valid if you specifiy an order or the rows. A table as such is not ordered, nor is the result of a query unless you specify an order by
Necromancing because the answers are outdated.
Take advantage of the LATERAL keyword introduced in PG 9.3
left | right | inner JOIN LATERAL
I'll explain with an example:
Assuming you have a table "Contacts".
Now contacts have organisational units.
They can have one OU at a point in time, but N OUs at N points in time.
Now, if you have to query contacts and OU in a time period (not a reporting date, but a date range), you could N-fold increase the record count if you just did a left join.
So, to display the OU, you need to just join the first OU for each contact (where what shall be first is an arbitrary criterion - when taking the last value, for example, that is just another way of saying the first value when sorted by descending date order).
In SQL-server, you would use cross-apply (or rather OUTER APPLY since we need a left join), which will invoke a table-valued function on each row it has to join.
SELECT * FROM T_Contacts
--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989
-- CROSS APPLY -- = INNER JOIN
OUTER APPLY -- = LEFT JOIN
(
SELECT TOP 1
--MAP_CTCOU_UID
MAP_CTCOU_CT_UID
,MAP_CTCOU_COU_UID
,MAP_CTCOU_DateFrom
,MAP_CTCOU_DateTo
FROM T_MAP_Contacts_Ref_OrganisationalUnit
WHERE MAP_CTCOU_SoftDeleteStatus = 1
AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID
/*
AND
(
(#in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo)
AND
(#in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom)
)
*/
ORDER BY MAP_CTCOU_DateFrom
) AS FirstOE
In PostgreSQL, starting from version 9.3, you can do that, too - just use the LATERAL keyword to achieve the same:
SELECT * FROM T_Contacts
--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989
LEFT JOIN LATERAL
(
SELECT
--MAP_CTCOU_UID
MAP_CTCOU_CT_UID
,MAP_CTCOU_COU_UID
,MAP_CTCOU_DateFrom
,MAP_CTCOU_DateTo
FROM T_MAP_Contacts_Ref_OrganisationalUnit
WHERE MAP_CTCOU_SoftDeleteStatus = 1
AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID
/*
AND
(
(__in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo)
AND
(__in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom)
)
*/
ORDER BY MAP_CTCOU_DateFrom
LIMIT 1
) AS FirstOE
Try using a subquery in your ON clause. An abstract example:
SELECT
*
FROM table1
JOIN table2 ON table2.id = (
SELECT id FROM table2 WHERE table2.table1_id = table1.id LIMIT 1
)
WHERE
...

PostgreSQL: find number of consecutive days up until now

Given a bunch of records (which represent checkins in my app) with a timestamp field, what would be a good way to determine the current streak of consecutive checkins?
In other words, with the checkins sorted by checkin time descending, how many records are there until a user missed a day?
Currently I'm using this technique:
SELECT distinct(uca.created_at::date) as created_at
FROM user_challenge_activities as uca INNER JOIN user_challenges as uc
ON user_challenge_id = uc.ID WHERE uc.user_id = #{user.id}
order by (uca.created_at::date) DESC;
...where I cast the checkin timestamps to a date (to end up with e.g. 2012-03-20), then in code, go through the records and increment a counter until the date between the record and the next record is greater than 1 day.
However, this approach seems clumsy to me, and it seems like the sort of thing that Postgres would excel at.
So is there in fact a better way to accomplish this?
with t as (
SELECT distinct(uca.created_at::date) as created_at
FROM user_challenge_activities as uca
INNER JOIN user_challenges as uc ON user_challenge_id = uc.ID
WHERE uc.user_id = #{user.id}
)
select count(*)
from t
where t.create_at > (
select d.d
from generate_series('2010-01-01'::date, CURRENT_DATE, '1 day') d(d)
left outer join t on t.created_at = d.d::date
where t.created_at is null
order by d.d desc
limit 1
)
let's try this again. will only generate series for the necessary range:
SELECT count(distinct(uca.created_at::date)) FROM user_challenge_activities as uca
JOIN
(SELECT generate_series(max(series_date),
(select max(user_challenge_activities.created_at)
FROM user_challenge_activities), '1 day') as datez
FROM
(SELECT generate_series(min(user_challenge_activities.created_at::date),
max(user_challenge_activities.created_at), '1 day')::date
as series_date
FROM user_challenge_activities) x
LEFT JOIN user_challenge_activities
ON (user_challenge_activities.created_at::date = x.series_date)
WHERE created_at IS NULL) d ON d.datez = uca.created_at::date
INNER JOIN user_challenges as uc ON user_challenge_id = uc.ID
WHERE uc.user_id = #{user.id};