How to check if id occurs in other table in MySQL - mysql

I have a table an_visit that has column visit_id. There is also a table an_transaction that has some visit_id too.
I would like to get a new column in MySQL based on if visit_id occurs in both tables. My approach:
SELECT visit_id, datetime_add, ISNULL((SELECT
1
FROM an_transaction
WHERE an_transaction.visit_id = an_visit.visit_id), 0)
FROM an_visit
WHERE datetime_add >= '2021-08-01'
LIMIT 50
But I got this error: MySQLdb.OperationalError: (1582, "Incorrect parameter count in the call to native function 'ISNULL'"). What do I do wrong, please?

SELECT visit_id, datetime_add, ISNULL((SELECT
1
FROM an_transaction
WHERE an_transaction.visit_id = an_visit.visit_id limit 1))
FROM an_visit
WHERE datetime_add >= '2021-08-01'
LIMIT 50
OR
SELECT visit_id, datetime_add, IFNULL((SELECT
1
FROM an_transaction
WHERE an_transaction.visit_id = an_visit.visit_id limit 1),0)
FROM an_visit
WHERE datetime_add >= '2021-08-01'
LIMIT 50

Related

MySQL Union two queries of same table

I have a problem. I created these 2 queries to get the start and end value:
Start value:
SELECT IF(`Order`.action = "Buy", `Order`.transMarketGross, `Order`.transMarketNet) AS startValue
FROM `Order`
WHERE agentId = (SELECT id FROM Agent WHERE owner = "Alexander") AND
`Order`.dateTimeExecuted <= DATE_SUB(curdate(), INTERVAL 1 MONTH)
ORDER BY `Order`.dateTimeExecuted DESC
LIMIT 1;
End value:
SELECT IF(`Order`.action = "Buy", `Order`.transMarketGross, `Order`.transMarketNet) AS endValue
FROM `Order`
WHERE agentId = ( SELECT id
FROM Agent
WHERE owner = "Alexander")
ORDER BY `Order`.dateTimeExecuted DESC LIMIT 1;
But now I want the start and end value in one result row, so I thought I could add UNION between the 2 queries:
SELECT IF(`Order`.action = "Buy", `Order`.transMarketGross, `Order`.transMarketNet) AS startValue
FROM `Order`
WHERE agentId = ( SELECT id
FROM Agent
WHERE owner = "Alexander") AND `Order`.dateTimeExecuted <= DATE_SUB(curdate(), INTERVAL 1 MONTH)
ORDER BY `Order`.dateTimeExecuted DESC LIMIT 1
UNION
SELECT IF(`Order`.action = "Buy", `Order`.transMarketGross, `Order`.transMarketNet) AS endValue
FROM `Order` WHERE agentId = ( SELECT id
FROM Agent
WHERE owner = "Alexander")
ORDER BY `Order`.dateTimeExecuted DESC LIMIT 1
Using these queries seperately, they do their job, but I get an error on the total query that this query is not valid. Can someone tell me what I am doing wrong and how I can fix this? Also if there are any improvements to simplify the query, let me know!
If you want one row, you might as well use two subqueries:
SELECT (SELECT (CASE WHEN o.action = 'Buy', o.transMarketGross, o.transMarketNet) AS startValue
FROM `Order` o JOIN
Agent a
ON o.agentId = a.id
WHERE a.owner = 'Alexander' AND
o.dateTimeExecuted <= DATE_SUB(curdate(), INTERVAL 1 MONTH)
ORDER BY o.dateTimeExecuted DESC
LIMIT 1
),
(SELECT (CASE WHEN o.action = 'Buy', o.transMarketGross, o.transMarketNet) AS startValue
FROM `Order` o JOIN
Agent a
ON o.agentId = a.id
WHERE a.owner = 'Alexander'
ORDER BY o.dateTimeExecuted DESC
LIMIT 1
);
Note that I made the following changes:
Added table aliases so the query is easier to write and read.
Qualified all column references, so the query is unambiguous.
Replaced the in with JOIN, which seems to be the intention.
Replaced double quotes with the SQL standard single quotes for string delimiters.
Use CASE (the standard) rather than the bespoke IF() for conditional logic.

mysql GROUP CONCAT not returning values

Here is my query
SELECT
SUM(o.order_disc + o.order_disc_vat) AS manualsale
FROM
orders o
WHERE
o.order_flag IN (0 , 2, 3)
AND o.order_status = '1'
AND (o.assign_sale_id IN (SELECT GROUP_CONCAT(CAST(id AS SIGNED)) AS ids FROM users WHERE team_id = 92))
AND DATE(o.payment_on) = DATE(NOW())
above query return null when i run this query in terminal
When i use subquery below it returns data
SELECT GROUP_CONCAT(CAST(id AS SIGNED)) AS ids FROM users WHERE team_id = 92)
above query returns
'106,124,142,179'
and when i run my first query like below
SELECT
SUM(o.order_disc + o.order_disc_vat) AS manualsale
FROM
orders o
WHERE
o.order_flag IN (0 , 2, 3)
AND o.order_status = '1'
AND (o.assign_sale_id IN (106,124,142,179))
AND DATE(o.payment_on) = DATE(NOW())
it return me value.
Why it is not working with subquery please help
This does not do what you want:
AND (o.assign_sale_id IN (SELECT GROUP_CONCAT(CAST(id AS SIGNED)) AS ids FROM users WHERE team_id = 92))
This compares a single value against a comma-separated list of values, so it never matches (unless there is just one row in users for the given team).
You could phrase this as:
AND assign_sale_id IN (SELECT id FROM users WHERE team_id = 92)
But this would probably be more efficently expressed with exists:
AND EXISTS(SELECT 1 FROM users u WHERE u.team_id = 92 AND u.id = o.assign_sale_id)
Side note: I would also recommend rewriting this condition:
AND DATE(o.payment_on) = DATE(NOW())
To the following, which can take advantage of an index:
AND o.payment_on >= current_date AND o.payment_on < current_date + interval 1 day

Trying to delete the records if greater then 25?

I am trying to delete all records so I only have the last 25 records. I am trying to figure this out but I can't seem to figure out, my MySQL version doesn't support the DELETE LIMIT OFFSET that most suggest, so I am trying to do
DELETE FROM
account_versions
WHERE
account_versions_id < (
SELECT
account_versions_id
FROM
account_versions
ORDER BY
account_versions_id
DESC
LIMIT 24, 1)
AND
account_id = 1
But I can't seem to get it to work. I want to only have the last 25 records for account_id = 1
So, as you said that account_versions_id is an autoincrement. Here is what you need:
DELETE FROM account_versions
WHERE account_id = 1
and account_versions_id IN
(select account_versions_id
from
(SELECT account_versions_id, #id:=#id+1 as rn
FROM account_versions,
( select #id:=0 ) a
ORDER BY account_versions_id DESC) x
where rn > 25)
Now it should work.

SQL: query with complex subqueries

I have the following tables in my game's database:
rankedUp (image_id, user_id, created_at)
globalRank (image_id, rank )
matchups (user_id, image_id1, image_id2)
All image_ids in globalRank table are assigned a rank which is a float from 0 to 1
Assuming I have the current logged in user's "user_id" value, I'm looking for a query that will return a pair of image ids (imageid1, imageid2) such that:
imageid1 has lower rank than imageid2 but is also the next highest rank less than imageid2
matchups table doesn't have (userid,imageid1,imageid2) or (userid,imageid2,imageid1)
rankedup table doesn't have (userid,imageid1) or if it does, the createdat column is older than X hours
What I have so far for requirement 1 is this:
SELECT lowerImages.image_id AS lower_image, higherImages.image_id AS higher_image
FROM global_rank AS lowerImages, global_rank AS higherImages
WHERE lowerImages.rank < higherImages.rank
AND lowerImages.image_id = (
SELECT image_id
FROM (
SELECT image_id
FROM global_rank
WHERE rank < higherImages.rank
ORDER BY rank DESC
LIMIT 1 , 1
) AS tmp
)
but it doesnt work because I can't reference higherImages.rank in the subquery.
Does anyone know how I could satisfy all of those requirements in one query?
Thanks for your help
EDIT:
I now have this query but I don't know about the efficiency and I need to test it for correctness:
SELECT lowerImages.image_id AS lower_image,
max(higherImages.image_id) AS higher_image
FROM global_rank AS lowerImages, global_rank AS higherImages
WHERE lowerImages.rank < higherImages.rank
AND 1 NOT IN (select 1 from ranked_up where
lowerImages.image_id = ranked_up.image_id
AND ranked_up.user_id = $user_id
AND ranked_up.created_at > DATE_SUB(NOW(), INTERVAL 1 DAY))
AND 1 NOT IN (
SELECT 1 from matchups where user_id = $userId
AND lower_image_id = lowerImages.image_id
AND higher_image_id = higherImages.image_id
UNION
SELECT 1 from matchups where user_id = $user_id
AND lower_image_id = higherImages.image_id
AND higher_image_id = lowerImages.image_id
)
GROUP BY 1
the "not in" statements I'm using are all indexed so they should run fast. The efficiency problem I have is the group by and selection of the global_rank tables
This question is a revision of Pretty Complex SQL Query, which should no longer be answered.
select
(
select image_id, rank from
rankedup inner join globalRank
on rankedup.image_id = globalRank .image_id
where user_id = XXX
limit 1, 1
) as highest,
(
select image_id, rank from
rankedup inner join globalRank
on rankedup.image_id = globalRank .image_id
where user_id = XXX
limit 2, 1
) as secondhighest
I normally use SQL Server, but this i think is the translation for mysql :)
This should do the trick:
SELECT lowerImages.*, higherImages.*
FROM globalrank AS lowerImages, globalrank AS higherImages
WHERE lowerImages.rank < higherImages.rank
AND lowerImages.image_id = (
SELECT image_id
FROM (
SELECT image_id
FROM globalrank
WHERE rank < higherImages.rank
ORDER BY rank DESC
LIMIT 1,1
) AS tmp
)
AND NOT EXISTS (
SELECT * FROM matchups
WHERE user_id = $user_id
AND ((image_id1 = lowerImages.image_id AND image_id2 = higherImages.image_id)
OR (image_id2 = lowerImages.image_id AND image_id1 = higherImages.image_id))
)
AND higherImages.image_id NOT IN (
SELECT image_id FROM rankedup
WHERE created_at < DATE_ADD(NOW(), INTERVAL 1 DAY)
AND USER_ID <> $user_id
)
ORDER BY higherImages.rank
I'm assuming the PKs of matchups and rankedup include all columns in those tables. This would allow the second 2 sub-queries to utilize the PK indexes. You would probably want an ordered index on globalrank.rank to speed up the first sub-query.

MySQL.. Return '1' if a COUNT returns anything greater than 0

I'm trying to create MySQL query that essentially returns true or false. I'd like to run
SELECT COUNT(id)
FROM comments
WHERE comment_date >= 1306904400
AND user_id = 1
So if the user posted in the forum 10 times this month, I'd like it to return just 1, otherwise I'd like it to return 0 to indicate they haven't.
Is that possible efficiently within SQL?
If you don't mind MySQL-specific things then you could use IF:
select if(count(id) >= 10, 1, 0)
from comments
where comment_date >= 130690440
and user_id = 1
Or MySQL booleans (which are 1 for true and 0 for false):
select count(id) >= 10
from comments
where comment_date >= 130690440
and user_id = 1
If you want to stick to standard SQL, then CASE is your friend:
select case when count(id) >= 10 then 1 else 0 end
from comments
where comment_date >= 130690440
and user_id = 1
Use CASE:
SELECT CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END AS `flag`
FROM `comments`
WHERE `comment_date` >= 1306904400 AND `user_id` = 1
You don't need to count all the matching records, just find if one exists. Unfortunately, MySQL doesn't appear to support IF EXISTS as Microsoft SQL Server does. However, you can use a subselect with LIMIT to fake it:
SELECT COUNT(1) AS has_comments FROM (SELECT id FROM comments WHERE comment_date >= 1306904400 AND user_id = 1 LIMIT 0, 1) t;
I know this is an old thread, but here's another way to accomplish this:
SELECT COUNT(id), 1 % (1 + count(id)) as greaterThanZero
FROM comments
WHERE comment_date >= 1306904400
AND user_id = 1
perhaps:
SELECT (SELECT COUNT(id) FROM comments
WHERE comment_date >= 1306904400 AND user_id = 1) > 10
I use a LIMIT to do something similar, and just evaluate it in my code. The SQL will stop after finding 10 matches.
SELECT id FROM {mytable}
WHERE comment_date >= 1306904400
AND user_id = 1
LIMIT 10
PHP requesting a minimum of 10 posts.
if(my_db_query() != 10)
not_enough_posts();