I have a query that returns a table that looks something like this:
+------+----------+-------+------+----+
| pID | name | month | q | s |
+------+----------+-------+------+----+
| 1468 | bob | 2 | 1 | 14 |
| 1469 | bob | 2 | 1 | 2 |
| 1470 | bob | 2 | 1 | 9 |
| 1468 | bob | 3 | 1 | 7 |
| 1469 | bob | 3 | 1 | 8 |
| 1470 | bob | 3 | 1 | 11 |
+------+----------+-------+------+----+
and I would like the output to be
+----------+-------+------+-----+
| name | month | q | sub |
+----------+-------+------+-----+
| bob | 2 | 1 | 25 |
| bob | 3 | 1 | 26 |
+----------+-------+------+-----+
Essentially, I want the first two columns of my output to be name, month and q grouped by name and month (they will always have the same data per line in this grouping) and I want the last column to be the SUM of s grouped by name only.
Thanks.
It should be something like this:
SELECT name, month, q, SUM(sub)
FROM table
GROUP BY name, month, q
Related
Given the sample table of:
+----+----------+---------+------+
| id | userName | storeId | cost |
+----+----------+---------+------+
| 1 | foo | 1 | 10 |
| 2 | bar | 1 | 10 |
| 3 | baz | 5 | 5 |
| 4 | baz | 3 | 20 |
| 5 | qux | 1 | 5 |
| 6 | qux | 4 | 20 |
| 7 | qux | 15 | 30 |
| 8 | qux | 17 | 40 |
| 9 | qux | 3 | 5 |
| 10 | quux | 6 | 20 |
+----+----------+---------+------+
I would like to work out how many people purchased at each store and how many did not. I want the report to display the results grouped by store.
I know the statement select storeId, count(distinct username) as total from purchases group by storeId provides me with how many people purchased in each store, but I want to subtract the result of the query select count(distinct userName) from purchases; in another column. I would expect the sample output to display as follows.
+---------+-----------+--------------+
| storeId | purchased | notPurchased |
+---------+-----------+--------------+
| 1 | 3 | 2 |
| 3 | 2 | 3 |
| 4 | 1 | 4 |
| 5 | 1 | 4 |
| 6 | 1 | 4 |
| 15 | 1 | 4 |
| 17 | 1 | 4 |
+---------+-----------+--------------+
You can use NOT condition with IN() function
As long a subse3lect gives back only only one,
you can use following
SELECT
storeId,
COUNT(DISTINCT username) AS total,
((SELECT
COUNT(DISTINCT userName)
FROM
purchases) - COUNT(DISTINCT username)) notPurchased
FROM
purchases
GROUP BY storeId
storeId | total | notPurchased
------: | ----: | -----------:
1 | 3 | 2
3 | 2 | 3
4 | 1 | 4
5 | 1 | 4
6 | 1 | 4
15 | 1 | 4
17 | 1 | 4
db<>fiddle here
I have 3 mysql tables named records, fields and fields_values.
Records table consists of meta data about records and record id.
Fields table
No. of fields for a record are variable, and can be added dynamically.
Fields values contain info about fields shown on the record form eg: what type of fields is it, whether it is required or no etc.
Fields values table
This table contains actual data of records for each field.
For example, these tables have data as below:
Records Table:
+----+-------------+------------+--------+
| id | category_id | created_by | status |
+----+-------------+------------+--------+
| 1 | 1 | 10 | 1 |
| 2 | 1 | 10 | 1 |
| 3 | 1 | 10 | 1 |
+----+-------------+------------+--------+
Fields Table:
+----+------------+------------+------+--------+----------+------------+
| id | title | alias | type | status | required | created_by |
+----+------------+------------+------+--------+----------+------------+
| 1 | First Name | first_name | text | 1 | 1 | 100 |
| 2 | Last Name | last_name | text | 1 | 1 | 100 |
| 3 | City | city | text | 1 | 1 | 100 |
| 4 | State | state | text | 1 | 1 | 100 |
| 5 | Country | country | text | 1 | 1 | 100 |
| 6 | Mobile | mobile | text | 1 | 1 | 100 |
+----+------------+------------+------+--------+----------+------------+
Fields Values Table:
+----+----------+-----------+------------+
| id | field_id | record_id | value |
+----+----------+-----------+------------+
| 1 | 1 | 1 | Andy |
| 2 | 2 | 1 | A |
| 3 | 3 | 1 | Manchester |
| 4 | 4 | 1 | NWE |
| 5 | 5 | 1 | UK |
| 6 | 6 | 1 | 1234567898 |
| 7 | 1 | 2 | Sandy |
| 8 | 2 | 2 | B |
| 9 | 3 | 2 | NYC |
| 10 | 4 | 2 | NY |
| 11 | 5 | 2 | USA |
| 12 | 6 | 2 | 1234567891 |
| 13 | 1 | 3 | Mandy |
| 14 | 2 | 3 | P |
| 15 | 3 | 3 | Mumbai |
| 16 | 4 | 3 | MH |
| 17 | 5 | 3 | IN |
| 18 | 6 | 3 | 1234567893 |
+----+----------+-----------+------------+
And, I want to records as below and want to sort it based on one of the fields as selected by user eg: country
+----+------------+-----------+------------+-------+-----------+------------+
| id | first_name | last_name | city | state | country ^ | mobile |
+----+------------+-----------+------------+-------+---------+------------+
| 3 | Mandy | P | Mumbai | MH | IN | 1234567893 |
| 1 | Andy | A | Manchester | NWE | UK | 1234567898 |
| 2 | Sandy | B | NYC | NY | USA | 1234567891 |
+----+------------+-----------+------------+-------+-----------+------------+
How do I sort such vertically stored data to order by one of field's values in a single query so that it can be shown horizontally?
Just to observe that it would be more usual to write that this way...
SELECT r.id
, MAX(CASE WHEN field_id = 1 THEN value END) first_name
, MAX(CASE WHEN field_id = 2 THEN value END) last_name
, MAX(CASE WHEN field_id = 3 THEN value END) city
, MAX(CASE WHEN field_id = 4 THEN value END) state
, MAX(CASE WHEN field_id = 5 THEN value END) country
, MAX(CASE WHEN field_id = 6 THEN value END) mobile
FROM records r
LEFT
JOIN fields_values v
ON v.record_id = r.id
GROUP
BY r.id;
Let's say i have a user table like this :
+----+-----------+----------------------+------+
| ID | Name | Email | Age |
+----+-----------+----------------------+------+
| 1 | John | john.doe1#mail.com | 24 |
| 2 | Josh | josh99#mail.com | 29 |
| 3 | Joseph | joseph410#mail.com | 21 |
| 4 | George | gge.48#mail.com | 28 |
| 5 | Joseph | jh.city89#mail.com | 24 |
| 6 | Kim | kimsd#mail.com | 32 |
| 7 | Bob | bob.s#mail.com | 38 |
| 8 | Joseph | psa.jos#mail.com | 34 |
| 9 | Joseph | joseph.la#mail.com | 28 |
| 10 | Jonathan | jonhan#mail.com | 22 |
+----+-----------+---------+------------+------+
In the actual, the database consists of more data and some of the data is duplicated, with more than two records. But the point is i want to get only the first and the second row of the duplicated rows that contains the name of "Joseph", How can i achieve this ? My code so far...
User::withTrashed()->groupBy('name')->havingRaw('count("name") >= 1')->get();
With that code the result will retrieve :
+----+-----------+----------------------+------+
| ID | Name | Email | Age |
+----+-----------+----------------------+------+
| 1 | John | john.doe1#mail.com | 24 |
| 2 | Josh | josh99#mail.com | 29 |
| 3 | Joseph | joseph410#mail.com | 21 |
| 4 | George | gge.48#mail.com | 28 |
| 6 | Kim | kimsd#mail.com | 32 |
| 7 | Bob | bob.s#mail.com | 38 |
| 10 | Jonathan | jonhan#mail.com | 22 |
+----+-----------+---------+------------+------+
And i use this code to try to get the second duplicated row :
User::withTrashed()->groupBy('name')->havingRaw('count("name") >= 2')->get();
The result is still same as the mentioned above :
+----+-----------+----------------------+------+
| ID | Name | Email | Age |
+----+-----------+----------------------+------+
| 1 | John | john.doe1#mail.com | 24 |
| 2 | Josh | josh99#mail.com | 29 |
| 3 | Joseph | joseph410#mail.com | 21 |
| 4 | George | gge.48#mail.com | 28 |
| 6 | Kim | kimsd#mail.com | 32 |
| 7 | Bob | bob.s#mail.com | 38 |
| 10 | Jonathan | jonhan#mail.com | 22 |
+----+-----------+---------+------------+------+
I want the result is to get record that have the id "5" with name "Joseph" like this :
+----+-----------+----------------------+------+
| ID | Name | Email | Age |
+----+-----------+----------------------+------+
| 1 | John | john.doe1#mail.com | 24 |
| 2 | Josh | josh99#mail.com | 29 |
| 4 | George | gge.48#mail.com | 28 |
| 5 | Joseph | jh.city89#mail.com | 24 |
| 6 | Kim | kimsd#mail.com | 32 |
| 7 | Bob | bob.s#mail.com | 38 |
| 10 | Jonathan | jonhan#mail.com | 22 |
+----+-----------+---------+------------+------+
But it seems only the first duplicate row is retrieved and i can't get the second duplicated row, can anybody give me suggestion ?
Let's start from your query
User::withTrashed()->groupBy('name')->havingRaw('count("name") >= 1')->get();
This will show all groups of rows whose count equals to 1 ore more. and this is the description of DISTINCT.
If you want to get only duplicate records you should get groups whose count is LARGER than 1.
The other thing to notice here is that a non-aggrigated column will be chosen randomly. because when you get a name and it's count, for example if you select name,count(name), email (email is not in the group by clause - not aggregated), and 4 rows have the same name. so you'll see:
+--------+-------------+-------+
| Name | Count(Name) | Email |
+--------+-------------+-------+
| Joseph | 4 | X |
+--------+-------------+-------+
what do you expect instead of X? which one of the 4 emails? actually, in SQLServer it's forbidden to select a non-aggrigated column and other databases will just give you a random one of the counted 3.
see this answer for more details it's explained very well: Do all columns in a SELECT list have to appear in a GROUP BY clause
So, we'll use having count(name) > 1 and select only the aggregated column name
DB::from('users')->select('name')->groupBy('name')->havingRaw('count("name") > 1')->get();
This should give you (didn't test it) this:
+--------+-------------+
| name | Count(name) |
+--------+-------------+
| Joseph | 4 |
+--------+-------------+
This will give you all names who have 2 or more instances. you can determine the number of duplicates in the having clause. for example having count(name) = 3 will give you all names which have exactly 3 duplicates.
So how to get the second duplicate? I have a question for that:
What is the first (original) duplicate? is it the one with the oldest created_at or the oldest updated_at ? or maybe some other condition?. because of that you should make another query with order by clause to give you the duplicates in the order most convenient to you. for example:
select * from `users` where `name` in (select `name` from users group by `name` having count(`name`) > 1) order by `id` asc
which will give:
+----+-----------+----------------------+------+
| ID | Name | Email | Age |
+----+-----------+----------------------+------+
| 3 | Joseph | joseph410#mail.com | 21 |
| 5 | Joseph | jh.city89#mail.com | 24 |
| 8 | Joseph | psa.jos#mail.com | 34 |
| 9 | Joseph | joseph.la#mail.com | 28 |
+----+-----------+---------+------------+------+
I have a table has name column and labels. Some names have several label (several names mean several row) so I need a query to got the names who have several label.
Could you help me please?
O.
Table:
+----+--------+-------+
| id | Name | Label |
+----+--------+-------+
| 1 | Juan | 10 |
| 2 | Joli | 11 |
| 3 | Sali | 12 |
| 4 | Juan | 15 |
| 5 | Odette | 13 |
| 6 | Sali | 18 |
| 7 | Sali | 17 |
| 8 | Youri | 14 |
+----+--------+-------+
Expected result:
+--------+-------+
| Name | Label |
+--------+-------+
| Juan | 10 |
| Juan | 15 |
| Sali | 12 |
| Sali | 18 |
| Sali | 17 |
+--------+-------+
Try this query.
SELECT name, label
FROM table2
WHERE name
IN (SELECT name
FROM table2
GROUP BY name
HAVING COUNT(name) > 1)
ORDER BY name ASC
I have a table that has some values in it, along with the time that value was taken against an associated ID from another table.
I am looking to retrieve the latest value for every item in that table, and then order by those latest values.
Here is an SQL fiddle, http://www.sqlfiddle.com/#!2/0be99
And here is text output.
'hist' table
| HIST_ID | HIST_ITEM_ID | HIST_VALUE | HIST_TIME |
|---------|--------------|------------|------------|
| 1 | 1 | 1 | 1420291000 |
| 2 | 1 | 2 | 1420292000 |
| 3 | 1 | 3 | 1420293000 |
| 4 | 1 | 5 | 1420294000 |
| 5 | 1 | 10 | 1420295000 |
| 6 | 1 | 50 | 1420296000 |
| 7 | 1 | 60 | 1420297000 |
| 8 | 1 | 77 | 1420298000 |
| 9 | 1 | 90 | 1420299000 |
| 10 | 1 | 101 | 1420300000 |
| 11 | 2 | 1 | 1420291000 |
| 12 | 2 | 3 | 1420292000 |
| 13 | 2 | 7 | 1420293000 |
| 14 | 2 | 9 | 1420294000 |
| 15 | 2 | 15 | 1420295000 |
| 16 | 2 | 21 | 1420296000 |
| 17 | 2 | 33 | 1420297000 |
| 18 | 2 | 35 | 1420298000 |
| 19 | 2 | 55 | 1420299000 |
| 20 | 2 | 91 | 1420300000 |
'items' table
| ITEM_ID | ITEM_TITLE |
|---------|------------|
| 1 | ABCD |
| 2 | XYZ123 |
So, I can do something like...
select * from hist
inner join items on hist_item_id = item_id
group by hist_item_id
order by hist_value desc
However this returns me a grouping that I cannot order. How can I order this grouping? I had a look at other similar questions on here but was unable to apply their solutions successfully to my query to produce the desire result.
The desired result here would be to return.
HIST_ITEM_ID | ITEM_TITLE | HIST_VALUE |
|------------|------------|------------|
| 1 | ABCD | 101 |
| 2 | XYZ123 | 91 |
You can use a join to get the most recent history item. Then you can join back to the history table and the item table to get additional information:
select h.*, i.item_title
from (select hist_item_id, max(hist_id) as max_hist_id
from hist
group by hist_item_id
) hh join
hist h
on h.hist_id = hh.max_hist_id join
items i
on i.item_id = hh.hist_item_id;
Here is a SQL Fiddle.
You should use MAX function and group by the item id. That would look like this:
SELECT i.item_id, i.item_title, MAX(h.hist_value)
FROM items AS i
INNER JOIN hist AS h
ON i.item_id = h.hist_item_id
GROUP BY i.item_id