SQL: Create Column (MySQL/PHPMyAdmin) - mysql

With the query:
SELECT TableA.ID, TableA.SensorID, TableA.Value, SensorIDs.Name, TableA.timestamp
FROM TableA
JOIN SensorIDs
ON TableA.SensorID = SensorIDs.ID // column 'Name' is in 'SensorIDs'
My result table looks like this:
ID | SensorID | Value | Name | timestamp
1 | 1 | 5 | A | 1000
2 | 2 | 10 | B | 1000
3 | 3 | 0 | C | 1000
4 | 1 | 1 | A | 2000
5 | 2 | 2 | B | 2000
6 | 3 | 6 | C | 2000
[..]
Is there a way to change my SQL query to get a table like this:
A | B | C | timestamp
5 | 10 | 0 | 1000
1 | 2 | 6 | 2000
Something with GROUP BY maybe?
EDIT: In the forseeable future there will be only these 3 values for 'Name'.
EDIT: RDBMS: MySQL-native (InnoDB), PHPMyAdmin
EDIT: Forgot to add column "SensorID" in the result.

I found the answer, by creating a PIVOT table with the tutorial I found here:
https://ubiq.co/database-blog/how-to-create-pivot-table-in-mysql/
SELECT time,
sum(IF(SensorID=1, Value, NULL)) AS Sensor1,
sum(IF(SensorID=2, Value, NULL)) AS Sensor2,
sum(IF(SensorID=3, Value, NULL)) AS Sensor3,
sum(IF(SensorID=4, Value, NULL)) AS Sensor4
FROM TableA
GROUP BY time

Related

Get greatest rows based on multiple columns in MySQL

I have a table that looks like:
id | title | value | language
---+-------+-------+---------
1 | a | 1800 | NULL
2 | a | 1900 | NULL
3 | b | 1700 | NULL
4 | b | 1750 | NULL
5 | b | 1790 | 1
6 | c | 1892 | NULL
7 | c | 1900 | 1
8 | c | 1910 | 2
9 | d | 3020 | NULL
Would like to have the following result:
id | title | value | language
---+-------+-------+---------
2 | a | 1900 | NULL
4 | b | 1750 | NULL
5 | b | 1790 | 1
6 | c | 1892 | NULL
7 | c | 1900 | 1
8 | c | 1910 | 2
9 | d | 3020 | NULL
The point is to select the greatest value in value column of every language of every title - greatest being the latest. Secondly, would like to avoid Aggregate functions like MAX, DISTINCT or GROUP-BY as I am building a MySQL View using the MERGE algorithm, and don't want to end up creating a temporary table (See the bottom section of https://dev.mysql.com/doc/refman/5.6/en/view-algorithms.html).
So far this works, but only returns greatest row per title:
SELECT t1.title
FROM table t1
LEFT OUTER JOIN table t2
ON t1.title = t2.title
AND t1.value < t2.value
WHERE t2.title IS NULL
How can I create one that takes language into account like the results above? Thanx.
You can do it with NOT EXISTS:
select t.*
from tablename t
where not exists (
select 1 from tablename
where
title = t.title and
coalesce(language, 0) = coalesce(t.language, 0) and
value > t.value
)
See the demo.
Results:
| id | title | value | language |
| --- | ----- | ----- | -------- |
| 2 | a | 1900 | NULL |
| 4 | b | 1750 | NULL |
| 5 | b | 1790 | 1 |
| 6 | c | 1892 | NULL |
| 7 | c | 1900 | 1 |
| 8 | c | 1910 | 2 |
| 9 | d | 3020 | NULL |
This answer assumes that you are using MySQL 8+, in which your query becomes very easy. MySQL 8 and later version support analytic functions, which were added with the intention to solve problems such as this.
We can try using ROW_NUMBER here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY title, language ORDER BY value DESC) rn
FROM yourTable
)
SELECT id, title, value, language
FROM cte
WHERE rn = 1;
Demo
There is a way to handle this with earlier versions of MySQL, but it requires user variables, and tends to be very ugly. So maybe consider upgrading if you expect to have many queries similar to this one.
This should give you what you want.
SELECT t1.title, t1.value, t1.language
FROM [Table] t1
LEFT OUTER JOIN [Table] t2 ON
t1.title = t2.title AND
(IFNULL(t1.language, '') = IFNULL(t2.language, ''))
WHERE
t1.value > t2.value;

MySQL Subselect Issue with two tables and aggregate functions into single query

I have 2 tables
Transaction table
+----+----------+-----+---------+----
| TID | CampaignID | DATE |
+----+----------+-----+---------+---+
| 1 | 5 | 2016-01-01 |
| 2 | 5 | 2016-01-01 |
| 3 | 2 | 2016-01-01 |
| 4 | 5 | 2016-01-01 |
| 5 | 1 | 2016-01-01 |
| 6 | 1 | 2016-02-02 |
| 7 | 3 | 2016-02-02 |
| 8 | 3 | 2016-02-02 |
| 9 | 5 | 2016-02-02 |
| 10| 4 | 2016-02-02 |
+----+----------+-----+---------+---+
Campaign Table
+-------------+----------------+--------------------
| CampaignID | DailyMaxImpressions | CampaignActive
+-------------+----------------+--------------------
| 1 | 5 | Y |
| 2 | 5 | Y |
| 3 | 5 | Y |
| 4 | 5 | Y |
| 5 | 1 | Y |
+-------------+----------------+--------------------
What I am trying to do is get a single random campaign where the the count in transaction table is less than the daily max impressions in the campaign table. I might also be passing a date s part of the query for the transaction table
So for CampaignId 1 there must be 4 trans of less in the transaction table and the Campaignactive must be a "Y"
Any help would be appreciated if this can be done in a single statement. ( mysql )
Thanks in advance,
Jeff Godstein
This should get it for you. The basic query is select each campaign that is active. The INNER query will pre-aggregate per campaign for the given date in question. From that, a LEFT-JOIN allows any campaign to be returned even if it does NOT exist within the subquery OR it DOES exist, but the count is less than that allowed for the date in question. The order by RAND() is obvious.
SELECT
c.CampaignID
from
Campaign c
LEFT JOIN
( select
t1.CampaignID,
count(*) as CampCount
from
Transaction t1
where
t1.Date = YourDateParameterValue
group by
t1.CampaignID ) as T
ON c.CampaignID = T.CampaignID
where
c.CampaignActive = 'Y'
AND ( t.CampaignID IS NULL
OR t.CampCount < c.DailyMaxImpressions )
order by
RAND()

Distinct order-number sequence for every customer

I have table of orders. Each customer (identified by the email field) has his own orders. I need to give a different sequence of order numbers for each customer. Here is example:
----------------------------
| email | number |
----------------------------
| test#com.com | 1 |
----------------------------
| example#com.com | 1 |
----------------------------
| test#com.com | 2 |
----------------------------
| test#com.com | 3 |
----------------------------
| client#aaa.com | 1 |
----------------------------
| example#com.com | 2 |
----------------------------
Is possible to do that in a simple way with mysql?
If you want update data in this table after an insert, first of all you need a primary key, a simple auto-increment column does the job.
After that you can try to elaborate various script to fill the number column, but as you can see from other answer, they are not so "simple way".
I suggest to assign the order number in the insert statement, obtaining the order number with this "simpler" query.
select coalesce(max(`number`), 0)+1
from orders
where email='test1#test.com'
If you want do everything in a single insert (better for performance and to avoid concurrency problems)
insert into orders (email, `number`, other_field)
select email, coalesce(max(`number`), 0) + 1 as number, 'note...' as other_field
from orders where email = 'test1#test.com';
To be more confident about not assign at the same customer two orders with the same number, I strongly suggest to add an unique constraint to the columns (email,number)
create a column order_number
SELECT #i:=1000;
UPDATE yourTable SET order_number = #i:=#i+1;
This will keep incrementing the column value in order_number column and will start right after 1000, you can change the value or even you can even use the primary key as the order number since it is unique all the time
I think one more need column for this type of out put.
Example
+------+------+
| i | j |
+------+------+
| 1 | 11 |
| 1 | 12 |
| 1 | 13 |
| 2 | 21 |
| 2 | 22 |
| 2 | 23 |
| 3 | 31 |
| 3 | 32 |
| 3 | 33 |
| 4 | 14 |
+------+------+
You can get this result:
+------+------+------------+
| i | j | row_number |
+------+------+------------+
| 1 | 11 | 1 |
| 1 | 12 | 2 |
| 1 | 13 | 3 |
| 2 | 21 | 1 |
| 2 | 22 | 2 |
| 2 | 23 | 3 |
| 3 | 31 | 1 |
| 3 | 32 | 2 |
| 3 | 33 | 3 |
| 4 | 14 | 1 |
+------+------+------------+
By running this query, which doesn't need any variable defined:
SELECT a.i, a.j, count(*) as row_number FROM test a
JOIN test b ON a.i = b.i AND a.j >= b.j
GROUP BY a.i, a.j
Hope that helps!
You can add number using SELECT statement without adding any columns in table orders.
try this:
SELECT email,
(CASE email
WHEN #email
THEN #rownumber := #rownumber + 1
ELSE #rownumber := 1 AND #email:= email END) as number
FROM orders
JOIN (SELECT #rownumber:=0, #email:='') AS t

MySQL Multiple Row Compare

I'm trying to run a query that will compare multiple rows in the same table and shows me all results that match.
For example, my data will look like this:
+-------------+-----------+---------+------------+-------+
| activity_id | d_id | tech_id | timestamp | value |
+-------------+-----------+---------+------------+-------+
| 39248078 | 1 | 1 | 2014-03-09 | 1 |
| 39248079 | 2 | 1 | 2014-03-06 | 1 |
| 39248082 | 3 | 1 | 2014-04-09 | 0 |
| 39248085 | 1 | 2 | 2014-03-13 | 1 |
| 39248088 | 3 | 2 | 2014-07-17 | 1 |
| 39248091 | 1 | 3 | 2014-02-07 | 1 |
| 39248093 | 2 | 3 | 2014-12-02 | 0 |
+-------------+-----------+---------+------------+-------+
The goal is to get all d_ids where tech_id = 3 AND (tech_id = 1 OR tech_id = 2). So in this case, the result should be 1 and 2 but not 3.
I've looked into subqueries but wasn't able to get it to work. Any help would be much appreciated.
SELECT
d_id
FROM table
WHERE d_id IN(
SELECT
d_id
FROM table
WHERE tech_id=3
) AND tech_id=1 OR tech_id=2
Let me know if it didn't work!
maybe something like this?
I havent tested it but from what I understand something like this should work
SELECT * FROM (
SELECT activity_id, d_id, tech_id, timestamp, value FROM test
WHERE tech_id = 3
) as temp
JOIN test t on t.d_id = temp.d_id
WHERE t.tech_id = 1 OR t.tech_id = 2
with CTE as
(
select * from table
where tech_id = 3
)
select c.d_id
from CTE c
where tech_id = 2 or tech_id = 3
Edited:
with CTE as
(
select * from table
where tech_id = 3
)
select c.d_id
from CTE c
where tech_id = 2 or tech_id = 3

Sum up values in SQL once all values are available

I have events flowing into a MySQL database and I need to group and sum the events to transactions and store away into another table. The data looks like:
+----+---------+------+-------+
| id | transid | code | value |
+----+---------+------+-------+
| 1 | 1 | b | 12 |
| 2 | 1 | i | 23 |
| 3 | 2 | b | 34 |
| 4 | 1 | e | 45 |
| 5 | 3 | b | 56 |
| 6 | 2 | i | 67 |
| 7 | 2 | e | 78 |
| 8 | 3 | i | 89 |
| 9 | 3 | i | 90 |
+----+---------+------+-------+
The events arrive in batches and I would like to create the transaction by summing up the values for each transid, like:
select transid, sum(value) from eventtable group by transid;
but only after all the events for that transid have arrived. That is determined by the event with the code e (b for the beginning, e for the end and i for varying amount of intermediates). Being a novice in SQL, how could I implement the requirement for the existance of the end code before the summing?
Perhaps with having:
select transid, sum(value)
from eventtable
group by transid
having max(case code when 'e' then 1 end)=1;
select transid, sum(value) from eventtable
group by transid
HAVING COUNT(*) = 3
you should count the records in the group. So when there is (b)egin, (i)?? don't know what it is and (e)nd this group is not filtered out.