I never used "if-else" or "case" inside a sql-query, but I guess I need to this time.
I have a table whose data represents something like a competition between two users:
//properties of "competition-table
int competitionId
int userId_Contrahent1
int userId_Contrahent2
otherdata....
Users can vote for one or the other contrahent within that competition; a vote is represented like so:
//properties of vote-table
int voteId
int competitionId
int userId_Voter
int userId_Winner // the user for which this vote counts
otherdata....
Obviously every given user in my Webapplication can only vote once for any given competition. So when I query for the competition I also want to have the information if the currentUser (which owns the current session) already voted for this competition. So in addition to all the other properties of competition I like to have an additional one, that has a key like "voted" and a value like "yes" or "no".
So my select statement should look something like this I guess:
SELECT competition.*,
If EXISTS (
SELECT * FROM vote WHERE userId_Voter = $currentUserId
AND competitionId = competition.competitionId)
...do something
FROM competition;
How do I do that exactly in MySQL?
SELECT c.*,
IF(v.competitionId IS NOT NULL, 'voted', 'not voted') AS `verdict`
FROM competition c
LEFT JOIN vote v ON v.competitionId = c.competitionId
AND v.userId_Voter = $currentUserId
Related
I am trying to query through my db to receive all posts which contain a certain user_id as owner or participant of the post.
My DB structure is as following:
id: INT
media_id: INT
owner_id: INT
participants: STRING eg. [1,2]
comments: TEXT eg. [{},{}]
Here are two example entries:
id: 1, media_id: 2, owner_id: 1, participants: "[1,2]", comments: "[]"
id: 2, media_id: 3, owner_id: 2, participants: "[2,1]", comments: "[]"
What I am trying to achieve is to get all rows where the user with the id 1 is part of the column owner_id OR participants
My current query looks as following:
SELECT * FROM posts WHERE owner_id = 1 OR participants IN "1"
And the result which I receive is only the line where 1 is the owner_id
I might understand the IN equasion wrong but 1 is a part of participants in both rows, therefore I should get both rows as result, shouldn't I?
This will do the trick:
SELECT * FROM posts WHERE owner_id = 1
OR participants like "%,1]"
OR participants like "[1,%"
OR participants like "%,1,%"
If you are willing to change your data structure for participants field to a comma separated value e.g. 1,2 or 2,1, you can then use:
SELECT * FROM posts WHERE owner_id = 1 OR FIND_IN_SET(1, participants);
You should change query to :
SELECT * FROM posts
WHERE (owner_id = 1 OR participants like "%,1" OR participants like "1,%" OR participants like "%,1,%")
You can use the KeyWord Like... the syntax example:
SELECT * FROM posts WHERE owner_id = 1 OR participants Like '%1%'
For other case you can use this:
SELECT * FROM posts WHERE owner_id = 1
OR participants like "%,1"
OR participants like "1,%"
OR participants like "%,1,%"
It is indeed not the correct approach of the IN clause. It should look like:
SELECT * FROM posts WHERE owner_id = 1 OR participants IN (1)
And its only need if looking for multiple values (in this case id's) like:
SELECT * FROM posts WHERE owner_id = 1 OR participants IN (1,5,15)
But because your participants column is a string it is not going to work, because MySQL is not parse/do something with the string to make it usable in the way you tried.
There is a possible and ugly way to achief what you want and do not change your database structure, but I do recommend to change it to a more usable approach with a relation table for a many-to-many relationship?
But the ugly way to achief it:
SELECT * FROM posts
WHERE owner_id = 1
OR (participants LIKE '[1,%' OR participants LIKE '%,1,%' OR participants LIKE '%,1]')
Better approach
A nice way good be to change your posts table slightly:
[posts]
id INT
media_id INT
owner_id INT
comments: TEXT eg. [{},{}]
And add a relational table what could like:
[post_participant]
post_id: INT
user_id: INT // -> or named participant_id
In this way you can get the proper posts with a query like:
SELECT p.* FROM posts p
LEFT JOIN post_participant pp ON pp.post_id = p.id AND pp.user_id = p.owner_id
WHERE p.owner_id = 1
OR pp.user_id IS NOT NULL
I have field that has value kind of store878 . I would like to have 878 from select statement. How do I get that numbers from select statement
select
store,
address
from
detail,
store_number
where
store (here i would like to have number) = store_mumber.id
I haven't tested this to make sure is works:
SELECT store, address
FROM detail d
INNER JOIN store_number s
ON CAST(SUBSTRING(d.store, LOCATE('%[0-9]%', d.store)) AS int) = s.id
But you should really consider changing the structure of your database.
The $userid of the currently logged in user is all the is currently available in the PHP code. I want to run a query against the mysql tables to return all of the status updates for myself and for friends ordered by createddate DESC.
MySQL Sample database tables:
[statusupdates]
statusupdateid int(8)
ownerid int(8)
message varchar(250)
createddate datetime
[friends]
friendid int(8)
requestfrom int(8)
requestto int(8)
daterequested datetime
dateupdated datetime
status varchar(1)
Question: Can I perform a single string query that returns each statusupdates.userid and the statusupdates.message ordered by statusupdates.createddate DESC?
Or do I have to run a query for each friends record where the $userid is in either the friends.requestfrom or friends.requestto then, run another query for alternate friends.requestfrom or friends.requestto (the one that doesn't include $userid), then sort all of the results by statusupdate.createddate and then get the statusupdates.message?
You want to look at MySQL Joins.
I think this may do something like what you're after, but it will almost definitely need debugging!
SELECT DISTINCT s.ownerid, s.message
FROM statusupdates s
LEFT JOIN friends f ON ($userid = f.requestfrom)
LEFT JOIN friends f ON ($userid = f.requestto)
ORDER BY s.createddate;
This is untested, but should work or at least get you in the right direction.
You could use IN() where you get a list of userids from a sub query. That subquery does a UNION on 2 queries - 1st to get the requestfrom userids, and 2nd to get the requestto userids. Finally we add an OR to include the current userid.
also, I assume that you also want to filter out where status = 1, as you don't want updates from those who have not confirmed friendships
SELECT s.ownerid, s.message
FROM statusupdates s
WHERE s.ownerid IN (
SELECT f1.requestfrom
FROM friends f1
WHERE f1.requestto = $userid
AND f1.status = 1
UNION
SELECT f2.requestto
FROM friends f2
WHERE f2.requestfrom = $userid
AND f2.status = 1
)
OR s.ownerid = $userid
ORDER BY s.createddate DESC
take a look at this sqlFiddle example - http://sqlfiddle.com/#!2/85ea0/3
The instructions are the following:
Generate a report of all the products purchased by the customers where it appears: the id of the customer, the customer's full name, the city, the state, the ID number, the date of sale, the product code, the product name, the quantity sold and finally a message that says "you paid" or "payment Pending" status depending on the payment status where 0 = paid and 1 = pending. This report should appear sorted alphabetically first by state and then by customer name.
what I tried is this:
select cli_nom, cli_city, cli_state, fac_num, fac_saledate, prod_cod, fac_total, fac_status
where fac_status = 0 as paid and fac_status = 1 as pending
from factures, products, clients order by cli_state, cli_nom, asc;
Wich absolutley didnt work, Im not sure about the sintax to rename or mask a column.
The table structures are the following:
table clientes:
1. cli_nom varchar(100)
2. cli_state varchar(100)
3. cli_city varchar(100)
4. cli_id int(11)
5. cli_status int(11)
6. cli_dateofsale date
table products:
1. prod_cod int(11)
2. prod_categ char(1)
3. prod_nom varchar(100)
4. prod_price double
5. prod_descrip varchar(100)
6. prod_discount float
table facturas:
1. fac_num int(11)
2. fac_datesold date
3. fac_cli_id int(11)
4. fac_status int
5. fac_total float
You are having a trouble with the querys.
When you want to query something, the form of the complete statement is something like this
Select [fields]
from [table(s)] --which means there includes inner joins
where [filter rows]
group by [fields to group]
having [filtering groups]
order by [fields]
Of course, is something much more complicated and bigger than this, but it will give you some initial concepts.
You will always have to respect this order, so in your query are putting a where into the select.
If you want to change something to show, dependending on some evaluation, but you will ALWAYS show something (you are not filtering, you are choosing what to show according to the value), you can use CASE clause.
In this example, you could do something like this
select cli_nom, cli_city, cli_state, fac_num, fac_saledate,
prod_cod, fac_total, fac_status
CASE when fac_status = 0 then 'You Paid'
when fac_status = 1 then 'payment Pending'
else 'Not sure about state' END
from factures
inner join products on --put here how do you relate products with factures
inner join clients on -- put here how do you relate clients with products/factures
order by cli_state, cli_nom, asc;
If you don't know how to use INNER JOIN, here you have some info.
Basically, is a clause that is used to relate two tables.
something like
(..)
from Table1 A
INNER JOIN Table2 B on A.id = B.id
(A and B are aliases, and representing the table that have set).
This means that it will compare every row from Table1 to every row from Table2, and when the condition is matched (in this case id from table1 [A.id] equals id from Table2 [= B.id]) then that relation-row is showed (means that it will show you all the row from table1 + all the row from table2)
Below is pseudo-code for SQL Server stored procedure I need to write:
int[] followers = (select FollowerID from StoreFollower where StoreId = #storeId)
insert into Message (senderId, recipientId)
values (#senderId, followers[0])
int rootMessageId = last_id()
foreach (int follower in followers.Skip(1))
insert into Message (senderId, recipientId, rootMessageId)
values (#senderId, follower, rootMessageId
It gets all Store's follower IDs, creates a record in Message for the first one. Then it creates a Message for each subsequent follower ID, also specifying ID of the first Message record in the batch.
I need to convert this to SQL Server stored procedure, however I never wrote one before so I'm hesitant. Should I use a table variable to hold select result? Should I use arrays? What is the closest match to foreach here? How do I slice off the first element?
I would very much appreciate a sketch of such proc, just to know what to look further at.
My stab at it in T-SQL. I assume that (a) FollowerID is int, (b) #storeId and #senderID ar parameters of the stored procedure.
DECLARE #FirstFollower int
DECLARE #FirstMessage int
--Get the first follower (i.e. smallest FollowerID, hopefully that's what you mean
--otherwise you need an appropriate ORDER BY clause here
SELECT #FirstFollower=TOP(1) FollowerId from StoreFollower
where StoreId = #storeId
--Store message for first follower and get root message id
INSERT INTO Message (senderId, recipientId)
VALUES(#senderId, #FirstFollower)
SELECT #FirstMessage=SCOPE_IDENTITY()
--store a message per follower except the first. Same conditions apply here
--regarding the order of the followers as in the first SELECT query
INSERT INTO Message(senderId, recipientId, rootMessageId)
SELECT #senderId, FollowerID, #FirstMessage
FROM StoreFollower WHERE
FollowerID <> #FirstFollower
HTH