I am coming into this project and have a little MySQL background to do basic SELECTs and INSERTs and whatnot. But, this is making me beat my head against the wall.
I have a typical user information table in MySQL:
USERS
+-------+----------+---------+-----+
|user_id|first_name|last_name|email|
+-------+----------+---------+-----+
1 tim jones tj#acme.com
2 sarah peteres sp#acme.com
3 larry doe ld#acme.com
Then I have multiple product tables:
PRODUCTS_ONE
+-------+-------+---------+----------+--------------+
|prod_id|user_id|prod_name|prod_width|prod_ship_date|
+-------+-------+---------+----------+--------------+
1 1 bowl nine 1-1-16
2 1 fork one 1-2-16
3 2 plate eleven 1-3-16
PRODUCTS_TWO
+-------+-------+----------+--------+--------------+
|prod_id|user_id|prod_state|prod_job|prod_ship_date|
+-------+-------+----------+--------+--------------+
1 3 maine min 1-1-16
2 2 texas max 1-2-16
3 1 ohio min 1-1-16
I have 15 total PRODUCT tables that all have prod_id, users_id, and prod_ship_date. The other fields might all be different based on what product table they are in. But, all the different PRODUCT tables have those three common fields.
What I am trying to accomplish is to get a list of USER info and PRODUCT info for products that match a certain ship date.
I want to find all the users and what product table and product id they are getting on a certain date.
So, if I searched on a ship date of 1-1-16, I would get something like:
+----------------+-----------+-------------+-----------+
|users.first_name|users.email|product_table|products_id|
+----------------+-----------+-------------+-----------+
tim tj#acme.com one,two 1,3
larry ld#acme.com one 3
If I searched on a ship date of 1-2-16, I would get something like:
+----------------+-----------+-------------+-----------+
|users.first_name|users.email|product_table|products_id|
+----------------+-----------+-------------+-----------+
tim tj#acme.com one 2
sarah sp#acme.com two 2
I hope this all makes sense. Unfortunately, I cannot change the structure or layout of the various product tables due to legacy issues.
I just can't figure out the MySQL statement to use to get something like this.
The above results will be used for reporting purposes.
you could write a query like this:
select first_name,email,prod_id,group_concat(product_table) as product_table from (
select u.user_id ,first_name,email,prod_id, 'one' as product_table from users u join products_one p on u.user_id = p.user_id where prod_ship_date = '2016-01-01 00:00:00'
union
select u.user_id,first_name,email,prod_id, 'two' as product_table from users u join products_two p on u.user_id = p.user_id where prod_ship_date = '2016-01-01 00:00:00'
) a
group by a.user_id
order by user_id,product_table
and so on.
With the second group concat and order by
select first_name,email,group_concat(prod_id),group_concat(product_table) as product_table from (
select u.user_id ,first_name,email,prod_id, 'one' as product_table from users u join products_one p on u.user_id = p.user_id where prod_ship_date = '2016-01-01 00:00:00'
union
select u.user_id,first_name,email,prod_id, 'two' as product_table from users u join products_two p on u.user_id = p.user_id where prod_ship_date = '2016-01-01 00:00:00'
) a
group by a.user_id
order by user_id,product_table
Check out this sqlfiddle
Related
My question sounds really easy, but I'm stuck.
Sample Data:
Listing:
id title State
1 Hotel with nice view Arizona
2 Hotel to stay Arizona
Review:
id listing_id rating mail_approved
1 1 4(stars) 1
2 1 4(stars) 0
3 1 3(stars) 1
4 2 5(stars) 1
So now I get the AVG value of the listings, but I want to get only the value of each listing when the review is mail_approved = 1. But when there is none review or no review with mail_approved = 1 it should give me the listing back just with 0.0 review points. So I would like to get all listing back if they have a review just calculate the AVG of those reviews with mail_approved = 1
How can I do this?
Do I have to rewrite the whole query?
Here is my query:
SELECT
ls.id,
title,
state,
ROUND(AVG(rating),2) avg_rating
FROM listing ls
JOIN review rv
ON ls.id = rv.listing_id
WHERE ls.state = '$get_state'
GROUP BY ls.id,
title,
state
ORDER BY avg_rating DESC
You used join, which is short for inner join. This type of join only gives results if a matching record exists in both tables. Change it to left join (short for left outer join), to also include listings without reviews.
You will need to move the state check and any other check to the join condition too, otherwise those listings without review will be dropped from the result again.
Lastly, you can coalesce the average value to get 0 instead of null for those records.
SELECT
ls.id,
title,
state,
COALESCE(ROUND(AVG(rating),2), 0) avg_rating
FROM listing ls
LEFT JOIN review rv
ON ls.id = rv.listing_id
AND ls.state = '$get_state'
AND ls.mail_approved = 1
GROUP BY ls.id,
title,
state
ORDER BY avg_rating DESC
As a side note, please check prepared statements (for PDO or MySQLi) for the proper way to pass input parameters to your query instead of concatenating with variables like $get_state. Concatting is error prone, and makes you more vulnerable for SQL injection.
Outer join the avarage ratings to the hotels:
select
l.id,
l.title,
l.state,
coalesce(r.avg_rating, 0)
from listing l
left join
(
select
listing_id,
round(avg(rating), 2) as avg_rating
from review
where mail_approved = 1
group by listing_id
) r on r.listing_id = l.id
where l.state = '$get_state'
order by avg_rating desc;
I have a table which stores clients like this:
id name
-- ----
1 John
2 Jane
...
I also have another table which stores links created by clients:
id client_id link created
-- --------- ---- -----------
1 1 ... 2015-02-01
2 1 ... 2015-02-26
3 1 ... 2015-03-01
4 2 ... 2015-03-01
5 2 ... 2015-03-02
6 2 ... 2015-03-02
I need to find how many links a client has created today, this month and during all the time. I also need their name in the result, so I'll be able to craete a HTML table to display the statistics. I thought I can code as less as possible like this:
$today = $this->db->query("SELECT COUNT(*) as today, c.id as client_id, c.name FROM `links` l JOIN `clients` c ON l.client_id = c.id WHERE DATE(l.created) = CURDATE() GROUP BY c.id");
$this_month = $this->db->query("SELECT COUNT(*) as this_month, c.id as client_id, c.name FROM `links` l JOIN `clients` c ON l.client_id = c.id WHERE YEAR(l.created) = YEAR(NOW()) AND MONTH(l.created) = MONTH(NOW()) GROUP BY c.id");
$yet = $this->db->query("SELECT COUNT(*) as yet, c.id as client_id, c.name FROM `links` l JOIN `clients` c ON l.client_id = c.id WHERE GROUP BY c.id");
And then merge them in PHP as I asked HERE before, like this:
$result = array_replace_recursive($today, $this_month, $yet);
So I'll be able to loop into the result and print my HTML table.
But there are logical problems here. Everything works fine, but the result in a month is a wrong number, forexample the whole created links of one person is 1 but it shows 4 in the monthly counter! I also tried to use RIGHT JOIN in SQL query to get all clients, so array_replace_recursive in PHP could work fine as I think it doesn't work properly at the moment, but no success and got wrong results again.
Can anyone show me a way to make the job done?
This query should do it for today
$query_today="
SELECT name, id AS user_id, (
SELECT COUNT( * )
FROM links
WHERE client_id = user_id AND created = '2015-03-02'
) AS alllinks
FROM clients"
adjust the WHERE clause in the subquery for months and all
$query_month="
SELECT name, id AS user_id, (
SELECT COUNT( * )
FROM links
WHERE client_id = user_id AND created like '2015-03%'
) AS alllinks
FROM clients"
$query_all="
SELECT name, id AS user_id, (
SELECT COUNT( * )
FROM links
WHERE client_id = user_id
) AS alllinks
FROM clients"
I can't get my head around this one!! So I'm seeking help....
For each user I'm trying to sum the types of posts assigned to them.
User Table: crm_users
Columns: users_id, users_first, users_last
Posts Table: crm_entities
Columns: crm_id, users_id, settype, post
I would like to COUNT() the total posts for a user where settype=draft as well as settype=published, for example:
Name------------Published---------Drafts
John Smith---------15---------------3
Nancy Grace--------11------------- 5
Jay Martin----------7--------------14
I am sure I am making this more difficult than it probably is... or at least I think !
Thanks for any advice!
Because MySQL uses 1 as representation for true and 0 for false, you could use:
SELECT
u.users_first,
u.users_last,
SUM(p.settype='published') as Published,
SUM(p.settype='draft') as Drafts
FROM
crem_users u
LEFT JOIN
crm_entities p
ON
u.users_id = p.users_id
GROUP BY
u.users_id
ORDER BY Published DESC;
Try:
select u.users_first, (select count(*) as qty from crm_entities e where e.settype='draft' and e.users_id = u.users_id) drafts, (select count(*) as qty from crm_entities e where e.settype='published' and e.users_id = u.users_id) published from crm_users u
I have 4 tables:
Table 1: Users
id
username
Table 2: Acts
act_id
act
user_id
act_score
act_date
Table 3: Votes
vote_id
act_id
user_voter_id
score_given
date_voted
Table 4: Comments
comment_id
comment
commenter_id
act_commented
date_commented
I want to show the contents of Acts Votes and Comments, based on User ID, combined in a list sorted in date order. Similar idea to Facebook's NewsFeed.
Sample output:
05-02-2014 10:00 Comment: "That's funny"
04-02-2014 12:30 Act Posted: "This is what I did"
04-02-2014 11:00 Comment: "Rubbish"
03-02-2014 21:00 Comment: "Looks green to me"
02-02-2014 09:00 Voted: +10 "Beat my personal best" by Cindy
01-02-2014 14:25 Act Posted: "Finally finished this darn website!"
I have tried to go down the create VIEW route to add all the required info to a table but
it was the wrong path. Now I'm not sure what to do!
Use UNION to combine separate queries. For example, to get the 10 most recent events across the three tables:
(
-- my acts
SELECT a.act_date timestamp,
'Act Posted' type,
a.act description,
u.username
FROM Acts a
JOIN Users u ON u.id = a.user_id
WHERE a.user_id = ?
ORDER BY a.act_date DESC
LIMIT 10
) UNION ALL (
-- votes on my acts
SELECT v.date_voted,
CONCAT('Voted ', v.score_given),
a.act,
u.username
FROM Votes v
JOIN Acts a USING (act_id)
JOIN Users u ON u.id = v.user_voter_id
WHERE a.user_id = ?
ORDER BY v.date_voted DESC
LIMIT 10
) UNION ALL (
-- comments on my acts
SELECT c.date_commented,
'Comment',
c.comment,
u.username
FROM Comments c
JOIN Acts a ON a.act_id = c.act_commented
JOIN Users u ON u.id = c.commenter_id
WHERE a.user_id = ?
ORDER BY c.date_commented DESC
LIMIT 10
)
ORDER BY timestamp DESC
LIMIT 10
first of all make id as a foreign key and use it in rest of the 3 tables while inserting data into those 3 tables.
like for Acts table,table structure should be like below.
Table 2: Acts
id //this is user id which is stored in the session while login.
act_id
act
user_id
act_score
act_date
The another thing to do is manage session for each and every user while he/she logged in.
Store user_id in the session for the further use like below.
session_start();
$_SESSION['user_id']=$_POST['ID'];
Then,fire select query for the particular table.I give you example of select query.
$sql="select * from Acts where id='".$_SESSION['id']."' ORDER BY act_date DESC";
$query=mysql_query($sql) or die("query failed");
Now, you will get result of Acts of particular user order by date.Then print it wherever you want.
I have an instrument list and teachers instrument list.
I would like to get a full instrument list with id and name.
Then check the teachers_instrument table for their instruments and if a specific teacher has the instrument add NULL or 1 value in a new column.
I can then take this to loop over some instrument checkboxes in Codeigniter, it just seems to make more sense to pull the data as I need it from the DB but am struggling to write the query.
teaching_instrument_list
- id
- instrument_name
teachers_instruments
- id
- teacher_id
- teacher_instrument_id
SELECT
a.instrument,
a.id
FROM
teaching_instrument_list a
LEFT JOIN
(
SELECT teachers_instruments.teacher_instrument_id
FROM teachers_instruments
WHERE teacher_id = 170
) b ON a.id = b.teacher_instrument_id
my query would look like this:
instrument name id value
--------------- -- -----
woodwinds 1 if the teacher has this instrument, set 1
brass 2 0
strings 3 1
One possible approach:
SELECT i.instrument_name, COUNT(ti.teacher_id) AS used_by
FROM teaching_instrument_list AS i
LEFT JOIN teachers_instruments AS ti
ON ti.teacher_instrument_id = i.id
GROUP BY ti.teacher_instrument_id
ORDER BY i.id;
Here's SQL Fiddle (tables' naming is a bit different).
Explanation: with LEFT JOIN on instrument_id we'll get as many teacher_id values for each instrument as teachers using it are - or just a single NULL value, if none uses it. The next step is to use GROUP BY and COUNT() to, well, group the result set by instruments and count their users (excluding NULL-valued rows).
If what you want is to show all the instruments and some flag showing whether or now a teacher uses it, you need another LEFT JOIN:
SELECT i.instrument_name, NOT ISNULL(teacher_id) AS in_use
FROM teaching_instrument_list AS i
LEFT JOIN teachers_instruments AS ti
ON ti.teacher_instrument_id = i.id
AND ti.teacher_id = :teacher_id;
Demo.
Well this can be achieved like this
SELECT
id,
instrument_name,
if(ti.teacher_instrument_id IS NULL,0,1) as `Value`
from teaching_instrument_list as til
LEFT JOIN teachers_instruments as ti
on ti.teacher_instrument_id = til.id
Add a column and check for teacher_instrument_id. If found set Value to 1 else 0.