flatten table into summary report with details in SQL (MySQL) - mysql

Is it possible to flatten out a database table for a report using sql (mysql). I can do with with php but it would be nice if I could do it simpler in sql.
example:
1) input: raw data - one line per cust/start & end time
cust start end
8000 0900 1000
8000 1000 1100
8000 1200 1300
9000 0900 1000
9000 1000 1100
2) required output: data flattened out - one line per customer with all start & end times following on same line
cust all related start-end times
---- ------------------------------
8000 0900-1000 1000-1100 1200-1300
9000 0900-1000 1000-1100

This is what you are looking for:
select cust, group_concat(concat(start,'-',end) SEPARATOR ' ')
from mytable
group by cust
See the working fiddle.

A SQL query has to have a fixed number of columns. This poses a problem in your case, because you don't know how many columns you need.
One workaround is to put all the results in one column. This produces a result like '090-1000,1000-1100,1200-1300'. You can do this with group_concat():
select cust,
group_concat(concat(start, '-', end) separator ', ' order by start) as StartEnds
from t
group by cust

Related

GCP Bigquery, I want to aggregate some values by a catogrical value in one column

I have these data that I want to analyze, schema as follows
timestamp price volume Qualifiers TradeCatogary
------------------- ----- ------ ---------- -------------
2016-07-11 01:00:00 3 2323 U OML
2016-07-11 01:02:03 2.5 434 K KCL
2016-07-11 01:03:34 4 3244 U KCL
2016-07-11 01:23:00 2.3 45 K OML
...
What I want is to calculate the total share, value of trade, trade count, and average price(VWAP = dollarTraded / totalVolume) of each 5min. Desired schema as follows
timestamp tradeCount totalVolume dollarTraded VWAP
--------- ---------- ----------- ------------ ----
This isn't hard and I managed it with SQL like this
SELECT
TIMESTAMP_SECONDS(CAST((ROUND(UNIX_SECONDS(Date_Time) / 300) * 300) AS int64)) AS interval_alias1,
count(*) AS cnt,
SUM(Volume) AS ShareVolumeTraded,
SUM(Price * Volume) AS DollarVolumeTraded,
(SUM(Price * Volume) / SUM(Volume)) AS VWAP
FROM
`dbd-sdlc-prod.HKG_NORMALISED.HKG_NORMALISED`
WHERE
RIC = '1606.HK'
AND Type="Trade"
AND (Date_Time BETWEEN TIMESTAMP('2016-07-11 00:00:00.000000') AND
TIMESTAMP('2016-07-11 23:59:59.999999'))
AND Volume >0
AND Price >0
GROUP BY
ROUND(UNIX_SECONDS(Date_Time) / 300), interval_alias1
ORDER BY interval_alias1
However, I want to push my analyze a bit further, I want not just a total summary of trades, I'd like to have more specific analyze based on different combination of qualifiers and tradeCatogary, such as:
count_U volume_U dollar_U VWAP_U count_U_OML volume_U_OML dollar_U_OML VWAP_U_OML ....
there are two qualifiers and two catogaries in the example so there will be:
3(basic) + 2 * 3(only qualifier) + 3 * 2 * 2(qualifier and catogary combination) = 21 columns
If there are no such trade in these time slot, the value will be left 0 as default
I wish to manage these queries with one SQL execution, I'm not sure how to address it, please give me a hint or some, thanks in advance
As mentioned in the comments, Group By Rollup is appropriate for your case. Group By Rollup returns the results of Group By on all possible combinations of mentioned columns. Since you also want the output as columns rather than rows, you can use pivot tables to change rows to columns. Also for your other requirement i.e. to concatenate the outputs of two columns and make it as a single column, CONCAT function can be used.

(SQL) Get comma separated specific value total count

I need to get the total number of occurrences by separate server ID like below :
-----------------------------
logID serversID
-------------------------------
1 50,51,51,50
2 51,52
3 50,50
I want a result like this:
ServerID Count
------------ ---------------
50 4
51 3
52 1
Thanks you for your help.
Fix your data model! A string is the wrong way to store multiple values. A string is the wrong way to store numbers. The correct way to represent this data is to use a second table, with one row per logid and serverid.
If you are stuck with this data model and you don't have a reference table for servers, you can split the values . . . painfully:
select substring_index(substring_index(t.serversid, ',', n.n), ',', -1) as server, count(*)
from (select 1 as n union all
select 2 union all
select 3 union all
. . . -- as many as the biggest list
) n join
t
on t.servers like concat(repeat('%,', n.n - 1), '%')
group by server;
Here is a db<>fiddle.

How to count the length of column seperated by ',' after using group_concat [duplicate]

This question already has answers here:
How to count items in comma separated list MySQL
(6 answers)
Closed 3 years ago.
I have a table looks like below:
ID path
| 1 YouTube,Newsletter,Social
| 2 YouTube,Newsletter
| 3 YouTube
Now I want to create a column to count the length of the path column. such as below:
ID path count weights
| 1 YouTube,Newsletter,Social 3 0.33
| 2 YouTube,Newsletter 2 0.5
| 3 YouTube 1 1
How do I do this?
I have tried JSON_LENGTH but couldn't get the command working.
PS. essentially I'm trying to replicate a query in PostgreSQL:
' select user_id, channels, 1.0 / array_length(channels, 1) as weights
from (
// ... query for marketing_channels as before)'
I am using MYSQL.
select d.email_entry_id
,d.channels
,JSON_LENGTH(d.channels)
from (
select email_entry_id
,group_concat(attribution_string order by visit_date asc separator ',' ) as channels
from database) d
error message: Error Code: 1370. execute command denied to user 'yb'#'%' for routine 'company.JSON_LENGTH'
Hope the question is clear enough. let me know if i need to clarify anything.
If I followed you correctly, you could simply extend the logic of your existing query (which, by the way, seems to be missing a GROUP BY clause). Instead of querying the aggregated data, it would be simpler to start from the original data, like:
SELECT
email_entry_id,
GROUP_CONCAT(attribution_string ORDER BY visit_date SEPARATOR ',' ) as channels,
COUNT(*) as `count`,
1/COUNT(*) as weight
FROM database
GROUP BY email_entry_id
There is a very common trick to achieve such outcome, demonstrated by following query
SELECT ID, PATH,
(LENGTH(PATH) - LENGTH(REPLACE(PATH, ',', ''))) + 1 COUNT
FROM DATABASE /* OR WHATEVER IS THE TABLE NAME */
The result

unpaid monthly salaries (mysql +vb.net)

I have a MySQL table some thing like that
NAME salary amount month 1 month 2 month 3 month 4
john 300 300 300 0 0
maria 400 400 0 0 0
tom 380 380 380 380 0
I wanna see results in table or list view or whatever like that
name unpaid month salary amount
john month 3 300
john month 4 300
maria month 2 400
maria month 3 400
maria month 4 400
tom month 4 380
I tried code like:
sql1="select name,month1 from table where month1=0 "
sql2="select name,month2 from table where month2=0"
sql3="select name,month3 from table where month3=0"
sql4="select name,month4 from table where month4=0"
Dim Sql = String.Concat(sql1, ";", sql2 ,";",sql2,";",sql4 )
but didn't work , any help pls ?
The syntax looks a little off in your code that you have now. I do not know if this is the exact code that you have in your program, but when the SQL statements are not properly formatted nothing will happen. I have made some changes to show what may be the issue.
sql1="SELECT name, month1, amount FROM table WHERE month1=0"
sql2="SELECT name, month2, amount FROM table WHERE month2=0"
sql3="SELECT name, month3, amount FROM table WHERE month3=0"
sql4="SELECT name, month4, amount FROM table WHERE month4=0"
Dim Sql = String.Concat(sql1, ";", sql2 ,";",sql2,";",sql4 )
The issue that I see with your current formatting is that you may want to have two separate tables for the name and the pay. With the separate tables you could then use foreign keys and join the two tables to have each name on every month with the amount they were paid that month. You would also be able to group the users based off of their name using GROUP BY
With the restructured table your call would be as simple as the statement below. Since I do not know your table names I have made fake ones for them.
Dim Sql = "SELECT NameTable.name, MonthTable.month, MonthTable.amount
FROM NameTable INNER JOIN MonthTable
ON {prmarykey for name} = {foreign key for month}
GROUP BY NameTable.name"
This should give you the result that you are looking for. Let me know if you have any questions or need clarification.
Try the UNION mysql aggregate :
sql1="select name,month1 as unpaid_month from table where month1=0 "
sql2="select name,month2 as unpaid_month from table where month2=0"
sql3="select name,month3 as unpaid_month from table where month3=0"
sql4="select name,month4 as unpaid_month from table where month4=0"
Dim Sql = String.Concat(sql1, " UNION ", sql2 ,"UNION ",sql2," UNION ",sql4
How you could use spaces before column names.
I would suggest you to use UNION.
But, here's the least version of SQL;
sql = "SELECT NAME, month1 as unpaid_month, salary_amount FROM tablename WHERE month1 = 0"
sql = sql & " UNION "
sql = sql & "SELECT NAME, month2 as unpaid_month, salary_amount FROM tablename WHERE month2 = 0"
sql = sql & " UNION "
sql = sql & "SELECT NAME, month3 as unpaid_month, salary_amount FROM tablename WHERE month3 = 0"
sql = sql & " UNION "
sql = sql & "SELECT NAME, month4 as unpaid_month, salary_amount FROM tablename WHERE month4 = 0"
But, the query is not good enough. What will happen if someone is paid half of its salary. And why you should get more than one record for a single person? Shouldn't there be any SUM for salary_amount and string concatenation for unpaid_month?
As it wasn't the part of your question, I can't post the advanced SQL here. Please ask for it in comment if you want that.
Please read these functions SUM() and GROUP_CONCAT() with temporary table AS TABLE. I think that you should use them for good programming.

Find number of items left based upon credit's id and all ids prior

I have a t variable which contains 100 and one table .
that table name credit and it contains the following data
Id
1
2
3
I would like the result set to look like this:
result
99 (100 - 1)
97 (100 - 2 - 1)
94 (100 - 3 - 2 - 1)
So far, I have been able to use the following code successfully:
set #t=100;
select #t:=#t-id as result from credit;
Is there a way to do this without using a variable?
This is quite simple and you shouldn't have to use the variable at all:
SELECT 100-(SELECT SUM(c2.id) FROM credit c2 WHERE c2.id <= c.id)
FROM credit c;
Here is a SQL Fiddle for you:
http://sqlfiddle.com/#!9/1fc3c6/6
The subquery simply gets the sum of all numbers including, and prior to the credit id.