Background:
i was first asked to do a website using mysql as the database, after that was done and sent, the clients asked me to convert it to mssql, and 3 tiers.
i did the classes and functions for transferring data between the servers, so the 3 tiers bit is about halfway done. the thing i'm struggling with now is the MsSQL.
Question:
the MySQL version of the question looks like this:
select a.id as a_id, a.first_name, a.last_name, a.agent_code,
b.id as b_id, b.user_id, b.status_admin
from tbl_user a
inner join tbl_testimonia b
on a.id = b.user_id
where b.status_admin=0
group by a.id
order by b.id desc
what it does is return testimonials that aren't approved yet, grouped to each user;
Where I am stuck:
i cant convert this to MSSQL
My best try:
Create view;
as in:
create view test as
(
select a.id as a_id, a.first_name, a.last_name, a.agent_code,
b.id as b_id, b.user_id, b.status_admin
from tbl_user a inner join tbl_testimonia b on a.id = b.user_id
where b.status_admin=0 and a.id in
(
select a.id from tbl_user a
inner join tbl_testimonia b
on a.id = b.user_id
where b.status_admin=0
group by a.id
)
)
this selects what i want, but the id field of the create view cant be grouped, meaning the id field is not unique and can have the same value.
I guess my question up to this point is, how do i uniquely select the id field in the view?
sure i can do it in PHP, but i could have done that a month ago when i first encountered the problem.
I've been trying to find the answer for a while now but cant seem to find the answer unique to my question
edit: the MSSQL doesn't work because even though the subquery is selecting unique ids the in statement in the main query makes this irrelevent
edit: example output::~ (in MySql the 3rd field is ommited)
| a_id | firstN | LastN | agent_code | b_id | user_id |status_admin|
+--------------------------------------------------------------------------+
| 32 | fn1 | ln1 | AC123213 | 14 | 32 | 0 |
| 41 | fn2 | ln2 | 12345678 | 15 | 41 | 0 |
| 32 | fn1 | ln1 | AC123213 | 16 | 32 | 0 |
edit: QUESTION SOLVED a big thx to Lieven for the simple answer
As you have already explained yourself, unlike SQL Server, MySQL allows grouping by with unaggregated expressions like this
SELECT *
FROM mytable
GROUP BY
column
As an unaggregated expression in GROUP BY returns an arbitrary record from each group and is not supposed to be used if the values vary you should be able to use the following statement as an equivalent in SQL Server
select a.id as a_id
, MIN(a.first_name)
, MIN(a.last_name)
, MIN(a.agent_code)
, MIN(b.id as b_id)
, MIN(b.user_id)
, MIN(b.status_admin)
from tbl_user a
inner join tbl_testimonia b on a.id = b.user_id
where b.status_admin=0
group by
a.id
order by
b.id desc
If you want to have non repetitive ids you can
select DISTINCT a.id from tbl_user a
inner join tbl_testimonia b
on a.id = b.user_id where b.status_admin=0
in your sub query
Related
I am trying to select the MAX of a COUNT grouping them by state (So one max for each distinct value in state). The count function works as intended.
SELECT c.id, c.name, t.name as type, COUNT(*) as count, c.state
FROM bookings_facilities f
JOIN bookings b
ON b.id = f.booking_id
JOIN clients c
ON c.id = b.client_id
JOIN client_types t
ON c.type = t.id
WHERE t.name = "School"
GROUP BY c.id
Here is the results,
I use the SQL statement below to try and choose the MAX of count grouping them by state.
SELECT *, MAX(z.count)
FROM (SELECT c.id, c.name, t.name as type, COUNT(*) as count, c.state
FROM bookings_facilities f
JOIN bookings b
ON b.id = f.booking_id
JOIN clients c
ON c.id = b.client_id
JOIN client_types t
ON c.type = t.id
WHERE t.name = "School"
GROUP BY c.id) z
GROUP BY z.state
Here is the results,
The 3 states, which appear only once in result 1 seems to be fine, but for the state, Selangor, which appears twice in the first result, had some problems.
The SQL query selected the right MAX(Count) which is 6, but instead of returning id as 1027 it returned id as 1002 which only has count as 1 in the first result.
I have tried it with different sets of data but I can't seem to get the details of the actual MAX(Count) row.
Here is the database design for reference
SQL Fiddle
Expected outcome is this, (Just the second row output needs to be changed).
Current Output Link
Since you can use MySQL 8.0, we can solve your problem using Window Functions. Over a partition of state, we will determine Row_Number() with the row having highest count as row number 1 and so on. Now, we simply need to consider only those rows where row number is 1, for a particular state.
Additionally, in your attempt, GROUP BY was not valid SQL. Older versions of MySQL were lenient and allowed it; but newer versions don't, unless you turn off the strict only_full_group_by mode. However, you should not disable it, and instead fix the query. The basic gist is that when using a Group By, your Select clause should only contain aggregated column(s)/expression(s) and/or the column(s)/expression(s) defined in the Group By clause. Do read: Error related to only_full_group_by when executing a query in MySql
SELECT dt2.id,
dt2.NAME,
dt2.state,
dt2.type,
dt2.count
FROM (SELECT dt1.id,
dt1.NAME,
dt1.state,
dt1.type,
dt1.count,
Row_number()
OVER (
partition BY dt1.state
ORDER BY dt1.count DESC) AS row_num
FROM (SELECT c.id,
c.NAME,
c.state,
t.NAME AS type,
Count(*) AS count
FROM bookings_facilities AS f
JOIN bookings AS b
ON b.id = f.booking_id
JOIN clients AS c
ON c.id = b.client_id
JOIN client_types AS t
ON c.type = t.id
WHERE t.NAME = 'School'
GROUP BY c.id,
c.NAME,
c.state,
type) AS dt1) AS dt2
WHERE dt2.row_num = 1
Result:
| id | NAME | state | type | count |
| ---- | ---------------------------------------- | --------- | ------ | ----- |
| 1006 | Holy Child Montessory School Of Fairview | Manila | School | 1 |
| 1027 | Sri Kuala Lumpur | Selangor | School | 6 |
| 1010 | Singapore American School | Singapore | School | 1 |
| 1015 | Keika Junior & Senior High School | Tokyo | School | 1 |
View on DB Fiddle
Hey I try to select a row from a table with two matching entries on another one.
The structure is as following:
----------------- ---------------------
| messagegroups | | user_messagegroup |
| | | |
| - id | | - id |
| - status | | - user_id |
| | | - messagegroup_id |
----------------- | |
---------------------
There exist two rows in user_messagegroup with the ids of two users and both times the same messagegroup_id.
I would like to select the messagegroup where this two users are inside.
I dont get it.. so I would appreciate some help ;)
The specification you provide isn't very clear.
You say "with the ids of two users"... if we take that to mean you have two user_id values you want to supply in the query, then one way to to find the messagegroups that contain these two specific users:
SELECT g.id
, g.status
FROM messagegroups g
JOIN ( SELECT u.messagegroup_id
FROM user_messagegroup u
WHERE u.user_id IN (42, 11)
GROUP BY u.messagegroup_id
HAVING COUNT(DISTINCT u.user_id) = 2
) c
ON c.messagegroup_id = g.id
The returned messagegroups could also contain other users, besides the two that were specified.
If you want to return messagegroups that contain ONLY these two users, and no other users...
SELECT g.id
, g.status
FROM messagegroups g
JOIN ( SELECT u.messagegroup_id
FROM user_messagegroup u
WHERE u.user_id IS NOT NULL
GROUP BY u.messagegroup_id
HAVING COUNT(DISTINCT IF(u.user_id IN (42,11),u.user_id,NULL)) = 2
AND COUNT(DISTINCT u.user_id) = 2
) c
ON c.messagegroup_id = g.id
For improved performance, you'll want suitable indexes on the tables, and it may be possible to rewrite these to eliminate the inline view.
Also, if you only need the messagegroup_id value, you could get that from just the inline view query, without the need for the outer query and the join operation to the messagegroups table.
I've got two tables, that are not linked the standard way (I'm aware this isn't a good way to do it)
lets say the tables are setup like the below
Table:
component
fields:
cid, cname, rangeid, company
Table: ranges
fields:
rid, rangename, year
While this is quite simple in a relational DB, i'm not too sure of the cleanest way to do this otherwise (remaking the DB is not an option).
the basic query I need is.
select * from component where range.year = '2014' and company = 'xxx'
any advice would be greatly appreciated.
Is this what you're looking for?
SELECT a.cid, a.cname, a.rangeid, a.company, b.rid, b.rangename, b.year
FROM component a
JOIN ranges b ON
b.rid = a.rangeid
WHERE b.year = 2014
AND a.company = 'xxx'
Result
| CID | CNAME | RANGEID | COMPANY | RID | RANGENAME | YEAR |
------|-----------|---------|---------|-----|-----------|------|
| 1 | Component | 1 | xxx | 1 | Range | 2014 |
Demo
If a range from component may not exist in ranges, then use a LEFT JOIN.
JOIN the two tables:
select c.cname, r.rangename, r.year, ...
from component AS c
INNER JOIN ranges AS r ON c.rangeid = r.rid
where r.year = '2014'
and c.company = 'xxx';
Note that: You can JOIN any tables normally, even if they haven't any relation between them, just put the condition in the ON clause, just like in your case. But, you have to ensure that indexes are setup correctly, see this page fore more information:
Multiple-Column Indexes
JOIN is what you are looking for :
select *
from component
inner join ranges on rid = rangeid and year = 2014
where company = 'xxx'
I have two tables: customer and mailing :
+==========+ +=============+
| customer | | mailing |
+==========+ +=============+
| id | | id |
+----------+ +-------------+
| name | | customer_id |
+----------+ +-------------+
| mailing_id |
+-------------+
Every time I send a mailing to a customer, I add a row in the mailings table with that mailing id. One mailing can be sent to multiple customers.
I want to have a sql call that returns all customers that have not yet received a certain mailing. How to ?
I am using mysql
select * from customer where id not in (
select customer_id from mailing where mailing_id = #mailing_id
)
Something like
Select c.ID, c.Name
From Customers C
left Join mailing m On c.id = m.customer_id
where m.customer_id is null
SELECT * FROM customers c
JOIN mailings m
ON c.id = m.id
WHERE NOT EXISTS (
SELECT id
FROM mailings i
WHERE i.id = c.id
GROUP BY i.id
)
What you describe is called an ANTI JOIN. Usually there are three different ways for formulating it in SQL: A NOT IN condition with a subquery, a NOT EXISTS condition with a correlated subquery, or a LEFT JOIN with a NOT NULL condition.
So for your query the possibilities are:
SELECT *
FROM customer
WHERE id NOT IN
( SELECT customer_id
FROM mailing)
SELECT *
FROM customer c
WHERE NOT EXISTS
( SELECT customer_id
FROM mailing m
WHERE m.customer_id = c.id)
SELECT *
FROM customer c
LEFT JOIN mailing m ON c.id = m.customer_id
WHERE m.customer_id IS NULL
This blog post compares the different possibilities with MySQL 5.1. According to it, LEFT JOIN / IS NULL and NOT IN are faster than NOT EXISTS.
However, you should try for yourself which one is the fastest. That always depends on your data, indexes, ...
I have following two tables 'USERS' and 'GROUPS':
USERS
-id
-name
-groupid
GROUP
-id
-name
I'd like to return all users along with their group's name and group id. It should be an outer join on group id field correct?
A simple INNER JOIN should be enough:
SELECT `USERS`.*, `GROUP`.name AS group_name
FROM `USERS`, `GROUP`
WHERE `USERS`.groupid = `GROUP`.id
You're going to want to look at the JOIN statement
Doing this from my phone, so pardon any moderately incorrect syntax, but something a long the lines of
Edit: other guy's syntax is better. It's too early here
You can use a LEFT JOIN between users and groups so that users who are not in a group still show up in the result set, but with group name and id NULL:
SELECT
a.*,
b.name AS group_name
FROM
users a
LEFT JOIN
`group` b ON a.group_id = b.id
Side note: Ensure that you're encasing the table name group in backticks because it is a reserved keyword.
The result-set should look something like:
id | name | group_id | group_name
-----------------------------------------------------------------------------
1 | John | 5 | ThisIsGroup5
3 | Tim | 3 | ThisIsGroup3
6 | NotInGroup | NULL | NULL
Changing LEFT to INNER in the above query would INNER JOIN the two tables and exclude the user "NotInGroup" from the result-set.