MySQL Count within an IF - mysql

+-------------+--------------+----------+-------+
| ticketRefNo | nameOnTicket | boughtBy | event |
+-------------+--------------+----------+-------+
| 38 | J XXXXXXXXX | 2 | 13 |
| 39 | C YYYYYYY | 1 | 13 |
| 40 | M ZZZZZZZZZZ | 3 | 14 |
| 41 | C AAAAAAA | 3 | 15 |
| 42 | D BBBBBB | 3 | 16 |
| 43 | A CCCCC | 3 | 17 |
+-------------+--------------+----------+-------+
+-------------+------------------+--------------+---------------------+--------+
| ticketRefNo | cardNo | cardHolder | exp | issuer |
+-------------+------------------+--------------+---------------------+--------+
| 38 | 4444111133332222 | J McKenny | 2016-01-01 00:00:00 | BOS |
| 39 | 4434111133332222 | C Dempsey | 2016-04-01 00:00:00 | BOS |
| 40 | 4244111133332222 | M Gunn-Davis | 2018-02-01 00:00:00 | RBS |
+-------------+------------------+--------------+---------------------+--------+
+-------------+-------------+----------+
| ticketRefNo | boxOfficeID | paidWith |
+-------------+-------------+----------+
| 41 | 1 | card |
| 42 | 2 | cash |
| 43 | 3 | chequ |
+-------------+-------------+----------+
I have a database with the data shown above. It represents a ticket-buying system. I would like to be able to see a list of tickets bought with the name of the event and either the boxOfficeID or the issuer of the debit card.
I have tried running the following code, to no avail.
SELECT t.ticketRefNo AS 'Reference', t.event AS 'Event',
IF(COUNT(SELECT * FROM Online WHERE t.ticketRefNo=o.ticketRefNo;) >= 1,
o.issuer, InPerson.boxOfficeID) AS 'Card Issuer or Box Office'
FROM Ticket AS t, InPerson, Online AS o
WHERE t.ticketRefNo=o.ticketRefNo;
Cheers in advance!

Some notes: the semicolon character isn't valid syntax; if you have a need to delimit the subquery, wrap it in parens. Escape column aliases like you'd escape any other identifier: use backticks, not single quotes. Single quotes are used around string literals.
Assuming that issuer in the Online table is NOT NULL, and assuming that ticketRefNo is unique in both the Online and InPerson tables, you could do something like this:
SELECT t.ticketRefNo AS `Reference`
, t.event AS `Event`
, IF(o.ticketRefNo IS NOT NULL,o.issuer,i.boxOfficeId)
AS `Card Issuer or Box Office`
FROM Ticket t
LEFT
JOIN InPerson i
ON i.ticketRefNo = t.ticketRefNo
LEFT
JOIN Online o
ON o.ticketRefNo = t.ticketRefNo
Use outer join operations to find matching rows in the InPerson and Online tables, and use a conditional test to see if you got a matching row from the Online table. A NULL will be returned if there wasn't a matching row found.

It's not a good idea to have one column JOINing to two different tables with some values in each of the two tables.
But here goes anyway:
( SELECT ... FROM Ticket t JOIN InPerson x USING(ticketRefNo) ... )
UNION ALL
( SELECT ... FROM Ticket t JOIN Online x USING(ticketRefNo) ... )
ORDER BY ...
The ALL assumes that InPerson and Online never have any overlapping ticketRefNos.
The ORDER BY an the end is in case you want to sort things, although I see no need for it in your attempted SELECT.
The two SELECTs must have the same number of columns.

Related

How to make IN condition check for set values

Basically I have two tables
MY_CHARACTER:
_________________________________________________
| char_ID | char_name | char_class | char_rank |
|------------------------------------------------|
| 1 | Aragorn | Fighter | 99 |
| 2 | Legolas | Archer | 90 |
| 3 | Smeagle | Spy | 20 |
|________________________________________________|
and
EQUIPMENT:
_________________________________________________
| equip_ID | equip_name | equip_owner | required |
|--------------------------------------------------|
| 1 | The one ring | 3 | 99 |
| 2 | Longsword | 1 | 90 |
| 3 | Waistcloth | 3 | 10 |
| 4 | Nazguls Mask | 2 | 95 |
|__________________________________________________|
Now what I want is to make a select command using IN condition, which will give me list of equipments that the characters are not eligible to use. So in this very example, it would be Smeagle being ineligible to wear the one ring and Legolas unable to wield the nazguls mask.
My command looks something like this
SELECT equip_name, equip_owner, required
FROM EQUIPMENT WHERE required IN (SELECT char_rank MY_CHARACTER);
Now this will only print out the equipment where required=char_rank however i want to print out a select, where required equipment level is higher than char_rank instead. Any idea how? Ideally using the IN condition.
EDIT: To clear out confusion regarding where I want, basically what my command does right now is it checks if EQUIPMENT.required=MY_CHARACTER.char_rank what I want instead is that it checks EQUIPMENT.required>MY_CHARACTER.char_rank
I would do this with a join:
select c.*, equip_id as notEligible
from my_character c join
equipment e
on c.char_rank < e.required;
So if you want a list of items, that a certain character is not allowed to use you can slightly modify your SQL:
SELECT
equip_name, equip_owner, required
FROM
EQUIPMENT
WHERE
required > (SELECT char_rank FROM MY_CHARACTER WHERE char_id=?);

SQL select default value when there is no such value

I have following tables in DB.
ACCOUNT TABLE
User_id| first_name | last_name | age |
_______|_____________|____________|_________|
1 | LeBron | James | 28 |
2 | Kobe | Bryent | 29 |
3 | Kevin | Durant | 30 |
4 | Jim | Jones | 31 |
5 | Paul | Pierce | 32 |
6 | Jeremy | Lin | 33 |
USER_BOOKMARK TABLE
User_id| Bookmarked_user_id
_______|____________________
1 | 2
1 | 3
1 | 4
2 | 1
2 | 4
3 | 1
5 | 6
I want to select user's information from ACCOUNT table and also whether that person is in my Bookmark list
ex) Lebron James wants to know Jeremy Lin's information and whether Jeremy is in he's bookmark lists.
Desired results =>
User_id| first_name | last_name | age | isBookmarked |
_______|_____________|____________|_________|______________|
6 | Jeremy | Lin | 33 | 0 | =>0 means no.
*It must return only one row.
*If user is on my bookmark list, value of isBookmarked is my user_id.
What I tried =>
SELECT ACCOUNT.user_id, ACCOUNT.firstname, ACCOUNT.lastname, coalesce(User_Bookmark.user_id, 0) as isBookmarked
FROM Account LEFT OUTER JOIN User_Bookmark ON Account.user_id = User_Bookmark.Bookmarked_user_id
WHERE Account.user_id=6 AND User_Bookmark.user_id=1
But this query returns zero rows... since I'm not an expert on sql, I assume that I'm missing something. Can anyone help me?
The User_Bookmark.user_id = 1 test is filtering out the non-matching rows, because that column will be NULL when there's no match. When doing a LEFT JOIN, you have to put conditions on the second table into the ON clause rather than WHEN.
SELECT ACCOUNT.user_id, ACCOUNT.firstname, ACCOUNT.lastname, coalesce(User_Bookmark.user_id, 0) as isBookmarked
FROM Account
LEFT OUTER JOIN User_Bookmark
ON Account.user_id = User_Bookmark.Bookmarked_user_id AND User_Bookmark.user_id=1
WHERE Account.user_id=6

SQL join several tables based on latest entry in transaction table per join record

I have a transaction table with timestamps
a transaction has one event and one user.
All transactions have an event,
All events have at least one trasaction,
Each transaction has a user that must exist,
A User will not necessarily have a transaction.
The output will be a sort of the evt list
Output line count should equal db.evt record count.
The first column of each table is the Autoinc unique index.
In transaction, these are fks to the other tables.
The problem is that I need the transaction with the latest timestamp for the evt in the transaction table.
I am still relatively new to SQL (Using MySQL) and while I muddle through joins. I have no idea how to get the latest record by evID by timestamp.
I have looked at other questions on the topic but not found one that addresses mine. (Granted there are 14K on Joins alone, so I may have missed one)
Sample Table Data below:
Table structure is hopefully obvious by I will edit it in if requested.
Edit:
I've changed the names of tables and columns for clarity (and to avoid matching keywords)
I tried Stuart's answer below and got an error:
Answer:
SELECT
eventTable.EvtName AS EvtD,
transTable.TranAct AS LastTrans,
userTable.UserName AS UsrNm
FROM
transTables,
INNER JOIN (
SELECT evtID, MAX(TransID) TransID FROM transTable GROUP BY evtID
) last ON last.evtID = transTable.evtID AND last.TransID = transTable.TransID
INNER JOIN eventTable ON eventTable.evtID = transTable.evtID
INNER JOIN userTable ON userTable.usId = transTable.usId
Response:
#1064 - 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
'INNER JOIN (
SELECT evtID, MAX(TransID) TransID FROM transTable GROUP BY evt'
at line 7
Tables:
db.transTable
| TransID | EvtID | TranAct | timestamp | UserID
----------------------------------------------------------------
| 1 | 1 | add | 2014-05-08 08:10:00.000 | 3
| 2 | 2 | add | 2014-05-08 09:10:00.000 | 2
| 3 | 3 | add | 2014-05-08 10:10:00.000 | 3
| 4 | 2 | validate | 2014-05-08 11:10:00.000 | 5
| 5 | 3 | validate | 2014-05-08 12:10:00.000 | 3
| 6 | 2 | reverse | 2014-05-08 13:10:00.000 | 1
| 7 | 1 | edit | 2014-05-08 14:10:00.000 | 4
| 8 | 4 | add | 2014-05-08 15:10:00.000 | 3
| 9 | 5 | add | 2014-05-08 16:10:00.000 | 2
db.eventTable
| EvtID | EvtName
-----------------
| 1 | Evt1
| 2 | Evt2
| 3 | Evt3
| 4 | Evt4
| 5 | Evt5
db.userTable
| UserID | UserName
--------------------
| 1 | Usr1
| 2 | Usr2
| 3 | Usr3
| 4 | Usr4
| 5 | Usr5
Desired output:
eventTable.EvtName AS EvtD
transTable.TranAct AS LastTrans
userTable.UserName AS UsrNm
| EvtD | LastTrans | UsrNm
--------------------------
| Evt1 | edit | Usr4
| Evt2 | reverse | Usr1
| Evt3 | validate | Usr3
| Evt4 | add | Usr3
| Evt5 | add | Usr2
Much thanks for any assistance.
Something like this shuold work where a derived table is used to eliminate all transactions except the latest per evId.
SELECT
eventTable.EvtName AS EvtD,
transTable.TranAct AS LastTrans,
userTable.UserName AS UsrNm,
FROM
transTable
INNER JOIN (
SELECT evId, MAX(UID) uid FROM transTable GROUP BY evId
) last ON last.evId = transTable.evId AND last.uid = transTable.uid
INNER JOIN eventTable ON eventTable.evId = transTable.evId
INNER JOIN userTable ON userTable.usId = transTable.usId

MySQL: optimize query for scoring calculation

I have a data table that I use to do some calculations. The resulting data set after calculations looks like:
+------------+-----------+------+----------+
| id_process | id_region | type | result |
+------------+-----------+------+----------+
| 1 | 4 | 1 | 65.2174 |
| 1 | 5 | 1 | 78.7419 |
| 1 | 6 | 1 | 95.2308 |
| 1 | 4 | 1 | 25.0000 |
| 1 | 7 | 1 | 100.0000 |
+------------+-----------+------+----------+
By other hand I have other table that contains a set of ranges that are used to classify the calculations results. The range tables looks like:
+----------+--------------+---------+
| id_level | start | end | status |
+----------+--------------+---------+
| 1 | 0 | 75 | Danger |
| 2 | 76 | 90 | Alert |
| 3 | 91 | 100 | Good |
+----------+--------------+---------+
I need to do a query that add the corresponding 'status' column to each value when do calculations. Currently, I can do that adding the following field to calculation query:
select
...,
...,
[math formula] as result,
(select status
from ranges r
where result between r.start and r.end) status
from ...
where ...
It works ok. But when I have a lot of rows (more than 200K), calculation query become slow.
My question is: there is some way to find that 'status' value without do that subquery?
Some one have worked on something similar before?
Thanks
Yes, you are looking for a subquery and join:
select s.*, r.status
from (select s.*
from <your query here>
) s left outer join
ranges r
on s.result between r.start and r.end
Explicit joins often optimize better than nested select. In this case, though, the ranges table seems pretty small, so this may not be the performance issue.

MySQL using GROUP BY to group by multiple columns

I'd like to use GROUP BY multiple columns, I think it's best to start with an example:
SELECT
eventsviews.eventId,
showsActive.showId,
showsActive.venueId,
COUNT(*) AS count
FROM eventsviews
INNER JOIN events ON events.eventId = eventsviews.eventId
INNER JOIN showsActive ON showsActive.eventId = eventsviews.eventId
WHERE events.status = 1
GROUP BY showsActive.venueId, showsActive.showId, showsActive.eventId
ORDER BY count DESC
LIMIT 100;
Output:
| *eventId* | *showId* | *venueId* | *count* |
+-----------+----------+-----------+---------+
[...snip...]
| 95 | 92099 | 9770 | 32 |
| 95 | 105472 | 10702 | 32 |
| 3804 | 41225 | 8165 | 17 |
| 3804 | 41226 | 8165 | 17 |
| 923 | 2866 | 5451 | 14 |
| 923 | 20184 | 5930 | 14 |
[...snip...]
What I would like instead:
| *eventId* | *showId* | *venueId* | *count* |
+-----------+----------+-----------+---------+
| 95 | 92099 | 9770 | 32 |
| 3804 | 41226 | 8165 | 17 |
| 923 | 20184 | 5930 | 14 |
So, I want my data grouped by eventId, but only once for each showId and venueId ...
I actually have a SQL query that does that, but it has 8 subqueries and is as slow as a T-Ford ... And since this is executed on every page load, speeding things up looks like a good idea!
There are a few questions like this, and I've tried many different things, but I've been at this query for an hour and I can't seem to get it to work as I want :-(
Thanks!
You probably want either a min or a max on showid, and then not include it in the group by, I can't tell which because looking at your "prefered" resultset, you have both.
If you want your data grouped by eventId, group just by eventId and you'll get exactly the result you're looking for.
This is a MySQL feature (?) that it allows you to select non-aggregate columns, in which case it will return the first row available. In other DBMS it's achieved by DISTINCT ON, which is not available in MySQL.