Getting the most recent message in every conversation [duplicate] - php

This question already has answers here:
SQL select only rows with max value on a column [duplicate]
(27 answers)
Closed 3 years ago.
I have a message table :
CREATE TABLE OneToOneMessages (
MessageID int NOT NULL AUTO_INCREMENT,
AuthorID int NOT NULL,
RecipientID int NOT NULL,
MessageText TEXT,
SentTime DATETIME,
PRIMARY KEY (MessageID),
FOREIGN KEY (AuthorID) REFERENCES Users(ID),
FOREIGN KEY (RecipientID) REFERENCES Users(ID)
);
I need to get the latest message from every conversation in order. I've found some queries online but I have no idea how they work and am not sure they get the job done.
I think I could just go one by one, getting the last message from each conversation and then sorting them by the sent time. If there was one query I could use that gets all the information, that would be best.

You can use a NOT EXISTS condition with a correlated subquery to filter the table. The condition ensures that no other record exists with the same sender/recipient or recipient/sender tuple and a greater send time:
SELECT t.*
FROM OneToOneMessages t
WHERE NOT EXISTS (
SELECT 1
FROM OneToOneMessages t1
WHERE
(
t.AuthorID = t1.AuthorID AND t.RecipientID = t1.RecipientID)
OR t.RecipientID = t1.AuthorID AND t.AuthorID = t1.RecipientID
)
AND t1.SentTime > t.SentTime
)
If you are using MySQL 8.0, this can be done potentially more efficiently using window function ROW_NUMBER():
SELECT *
FROM (
SELECT
t.*,
ROW_NUMBER() OVER(
PARTITION BY LEAST(AuthorID, RecipientID), GREATEST(AuthorID, RecipientID)
ORDER BY SentTime DESC
) rn
FROM OneToOneMessages t
)x
WHERE rn = 1
The inner query ranks records by descending send time within groups of records having the same sender/recipient or recipient/sender tuple. The outer query filters only on the top record in each group.

Related

MySQL/PHP: Finding IDs that are NOT stored in Database [duplicate]

This question already has answers here:
MySQL get missing IDs from table
(8 answers)
Closed 4 months ago.
I am using a MySQL database with a table containing random records. The only column that is interesting for my use case is a BIGINT column called "ID". This is also the primary key of that table, but it is not an AUTO-INCREMENT column and since this data is fetched from external sources, these IDs are not continuously.
Sample-Data from that Table:
[ID]
201101
201504
201641
201755
...
I need to find an efficient way to find all the IDs that are NOT yet stored in that database within a specific range. For instance (pseudo):
GetUnusedIDs(RangeStart = 201100; RangeEnd = 201600);
-> 201100
-> 201102
-> 201103
-> ...
-> 201503
-> 201505
-> ...
-> 201600
What I did so far was fetching all values within that range into a PHP-Array, then within a FOR-Loop from RangeStart to RangeEnd checking for each number if it is contained in that specific array and if not, adding it to a new array containing only the numbers that don't yet exist in the database.
I think there must be a better (more efficient) way to do this.
Thank you in advance!
You can do this within MySQL by creating the sequential integers in a seed table and checking which don't exist in your main table, be sure to filter the seed table by the minimum and maximum to increase performance, and alter the number of cross joins to fit the range
with seed as (
select null as n
union all select null
union all select null
union all select null
union all select null
),
numbers as (
select row_number() OVER ( ORDER BY a.n ) + "YOUR_MIN_ID" n
from seed a,
seed b,
seed c,
seed d,
seed e,
seed f,
seed g
)
select n from numbers where not exists ( select null from Table where n = ID )

Updating / inserting once and then exit

Hope some of you pros can help me out on this sql / php issue.
The short version:
I need to add members to a task-database. So I have memberlist, it loop through each member and runs below sql.
I need to run an SQL statement that is to exit after first update / execution where it hits the parameters. So I need some kind of return for each time the sql updates a field?
Pseudocode:
Update this column
condition 1
condition 2
after first execution exit
Current sql:
UPDATE calendar
SET spil1 = '$temp'
WHERE spil1 IS NOT NULL
AND
(dayname = 'Lørdag'
OR dayname = 'Søndag')
// now exit if the above is met and the sql update was executed.
So the problem is I cannot make it stop (tried limit, top etc)
How is this made with SQL? or is there a smart way to condition it in the PHP loop before executing the script?
if you are using any unique id put this code at the end of your query..
good luck.
AND unique_id IN ( SELECT unique_id FROM calendar order by unique_id ASC LIMIT 1 )
Do you have an id column in calendar? If yes use the following query (Not tested):
UPDATE calendar
SET spil1 = '$temp'
WHERE id =
(SELECT id FROM
(SELECT * FROM calendar
WHERE spil1 IS NOT NULL
AND
(dayname = 'Lørdag'
OR dayname = 'Søndag')
LIMIT 1)T)
What this query does, it brings the first record that applies to your condition, and then update that record the way you want it
I'm pretty sure you want to assign a different member to each NULL value in the calendar table. This is tricky. It requires enumerating the rows in each table for the join -- and assumes a unique id in the calendar table.
update calendar c join
(select c.*,
row_number() over (order by c2.pil1) as seqnum
from calendar c2
where c2.pil1 is not null and
c2.dayname in ('Lørdag', 'Søndag')
) c2
on c.calendar_id = c2.calendar_id join -- the unique id
(select ml.*,
row_number() over (order by ml.member_id) as seqnum
from memberlist ml
) ml
on ml.seqnum = c2.seqnum
set c.spil1 = ml.member_id;
I also suspect that you want the condition for the calendar table to be IS NULL rather than IS NOT NULL, but this is the logic you have in the question.

Select *, count(*) in one Query [duplicate]

This question already has an answer here:
SQL counting all rows instead of counting individual rows
(1 answer)
Closed 5 years ago.
I've got a DB with a few columns and I'm trying to populate a html table with it.
Everything's going fine but I've encountered the following problem:
Since I'm filling filtered Results into different Columns, I came up with a SQL Query that needs both Select * and count(*)?
$query = "SELECT *, COUNT(example_A) AS total_example_A FROM test WHERE example_A = 'certain_result' AND date(start_date) = '$current_date_proof' ORDER BY start_date ASC";
It does work, but I'm only getting the first result. I guess I cannot combine Select with Count?
You can do it with a correlated sub-query, Count is an aggregation function ( so it aggregates or combines all the data ):
$query = "
SELECT
t1.*,
( SELECT COUNT(t0.id) FROM test AS t0 WHERE t0.id = t1.id ) AS total_example_A
FROM
test AS t1
WHERE
t1.example_A = 'certain_result'
AND
date(t1.start_date) = '$current_date_proof'
ORDER BY t1.start_date ASC
";
This assumes that your table test has a primary key named id. One other thing is I would count on the primary key if its not (example_A) COUNT(t0.id)
In my world a database either have a Auto Increment Int as the primary key or they have a compound primary key consisting of 2 or more foreign keys which are themselves Auto Increment Int fields. It's vital ( IMO ) to always have a surrogate key in you table. That is a key that has no direct relationship to the data itself. But, that is just me...
You could just count the return within your application, but barring that the correlated sub-query should give you the best/goodest performance. Certainly much better then a separate database call.

How do I delete duplicate rows in SQL? [duplicate]

This question already has answers here:
Remove duplicate rows in MySQL
(26 answers)
Closed 7 years ago.
I am trying to delete duplicate rows using SQL, whilst leaving one of the rows behind.
The table I am trying to delete duplicate rows from is called table "A" made up of:
A.AID, A.BID, A.AName, A.AType, A.APrice.
In this table I have a number of duplicate rows with all of the data exactly the same apart from the A.ID.
I am trying to create a query that will look for duplicates and then remove the duplicate making sure one of the rows are left behind. I am using phpMyAdmin and MySQL.
DELETE FROM member
WHERE id IN (SELECT *
FROM (SELECT id FROM member
GROUP BY member_id, quiz_num, question_num, answer_num HAVING (COUNT(*) > 1)
) AS A
);
use group by and count
DELETE FROM
YourTable
WHERE
AID NOT IN ( SELECT
MAX(AID)
FROM
YourTable
GROUP BY
BID ,
AName ,
AType ,
APrice );
Consider the query below, this will remove all the duplicate rows and prevents any future duplicate row.
ALTER IGNORE TABLE A ADD UNIQUE INDEX index_name (A.AID, A.BID, A.AName, A.AType, A.APrice );

SELECT from various tables, with a list of indexes

Here is the scenario.
I'm developing a timeclock system; I have these tables:
-punch (id_punch,date)
-in1 (id_in1,time,id_punch,...)
-in2 (id_in2,time,id_punch,...)
.
-in6 (id_in6,time,id_punch,...)
-out1 (id_out1,time,id_punch,...)
-out2 (id_out2,time,id_punch,...)
.
-out6 (id_out6,time,id_punch,...)
My question is, how can I with only one query in PHP to get all values from in and out table, from a list of id_punch values, for example:
Get all punchs of September, or
Get all punchs of July to December,
I mean... from a list of id_punch between two dates, get all the results from the in, out table.
The only way I think is to do a query with each id_punch variable, but in a month its about 20-25 queries... to much?
To get all the data from the tables you'll need to join them with JOIN MySQL JOIN
But from what I can gather by looking at you tables, you probably should be thinking about making this into one table rather than the multiple tables you have here.
You really need to store all the in/out data in one table that is a child of punch:
CREATE TABLE punch (
id_punch SERIAL PRIMARY KEY,
punch_date DATE NOT NULL,
ip_address INT UNSIGNED NOT NULL
-- plus other attributes
) ENGINE=InnoDB;
CREATE TABLE inout (
id_punch BIGINT UNSIGNED,
in_time TIME NOT NULL,
out_time TIME NULL,
PRIMARY KEY (id_punch, in_time),
FOREIGN KEY (id_punch) REFERENCES punch (id_punch)
) ENGINE=InnoDB;
Now you can query very easily for all punches in September:
SELECT *
FROM punch LEFT OUTER JOIN inout USING (id_punch)
WHERE EXTRACT(YEAR_MONTH FROM punch_date) = '200909';
Your database schema is a little unclear, but if you're asking how to get the results corresponding to a list of ids you already have this should work (assuming your ids are 1,3,5,7,9)
SELECT * FROM table1, table2, table 3
WHERE table1.punch_id = table2.punch_id AND table2.punch_id = table3.punch_id AND table3.punch_id IN (1,3,5,7,9)
you'll probably need to modify it just make sure every table's punch_id is joined to that IN constraint
I can't use one table cause i have some informations in each punch, as ipaddress, and other information.
Neil, the answer was in my nose, i already saw a solution like yours, but my doubt is how to put the list in the query, answer for my own question = use a foreach() in php to "populate" this list...
Something like:
> SELECT * FROM table1, table2, table 3 WHERE table1.punch_id = table2.punch_id AND table2.punch_id = table3.punch_id AND table3.punch_id IN (<? foreach($query->results() as $row) echo $row->id_punch;?>)
im using codeigniter

Categories