mysql display multiple rows in one row - mysql

I have a table tbl_usi in mysql with records as below:
present_date usi_value deal_count
----------------------------------------------------------
2015-10-13 b1 c1
2015-10-12 b2 c2
2015-10-11 b3 c3
I want to write a query that will do this using present_date field to select the present date and the date before it and display them together:
present_date usi_value deal_count previous_date previous_usi_value previous_deal_count
----------------------------------------------------------
2015-10-13 b1 c1 2015-10-12 b2 c2
2015-10-12 b2 c2 2015-10-11 b3 c3
2015-10-11 b3 c3 2015-10-10 b4 c4
How do I achieve this. Thanks

Select everything from your table, then join it to itself, making sure the 2 joined tables are given different names so you can distinguish them (I used 'a' and 'b' here). The join offsets the dates by 1 day. Then you can select the fields you want from the joined table.
select
a.present_date,
a.usi_value,
a.deal_count,
b.present_date as previous_present_date,
b.usi_value as previous_usi_value,
b.deal_count as previous_deal_count
from
tbl_usi as a
left join tbl_usi as b
on b.present_date = a.present_date - interval 1 day;
If you didn't already have one before, you will now want an index for the present_date column too BTW.

Alternative, which works when there are date gaps.
select
a.present_date,
a.usi_value,
a.deal_count,
b.present_date as previous_present_date,
b.usi_value as previous_usi_value,
b.deal_count as previous_deal_count
from
tbl_usi as a
join tbl_usi as b
where
b.present_date = (select max(present_date) from tbl_usi where present_date < a.present_date);
As with previous solution the same table is joined twice, but this time the previous row is found by way of a subquery.

Related

Can I use a column dynamically using CASE WHEN in a GROUP BY query?

The query statement I want is whether GROUP BY can be changed according to the value of the selected column.
For example, The query I was thinking of is as below,
SELECT * FROM (
SELECT DATE, A,sum(B),C FROM TABLE
GROUP BY DATE
CASE WHEN C ='1' THEN A,B
ELSE '2' THEN A END.
)
If C is 1, the final query is
SELECT DATE, A,sum(B),C FROM TABLE
GROUP BY DATE, A
If C is 2, the final query is
SELECT DATE, A,sum(B),C FROM TABLE
GROUP BY DATE
If the table has data like below,
DATE A B C
---- ------------------
2022.01 A1 50 1
2022.01 A1 50 1
2022.01 A2 30 1
2022.01 A2 30 1
2022.02 A1 20 2
2022.02 A1 20 2
2022.02 A2 25 2
2022.02 A2 25 2
2022.03 A1 10 1
2022.03 A2 20 1
The query is applied like this according to each C value, so the result I want is
DATE A B C
---- -----------------------
2022.01 A1 100 1
2022.01 A2 60 1
2022.02 (A1+A2) 90 2
2022.03 A1 10 1
2022.03 A2 20 1
want Can you make a sql query with this syntax?
To change and apply the columns grouped according to the C value.. ;(
You might be able to get the result you want by combining the result of two queries using UNION ALL. The exact syntax may differ (sometimes you have to select from a subquery) and you may want to specify ordering but I think this could work.
SELECT DATE,A,B,C
FROM Table
WHERE C = '1'
GROUP BY DATE,A,B
UNION ALL
SELECT DATE,A,B,C
FROM Table
WHERE C = '2'
GROUP BY DATE,A;
EDIT:
You can play around by adding more data and adjust the column types, names, etc. according to your needs. Here's the link to the MySql 5.7 fiddle I used to test the query.
https://www.db-fiddle.com/f/gpu8cUo5wRa2Kxn86Ghjde/0
CREATE TABLE ChodingTable(SomeDate DATE, A VARCHAR(2), B INTEGER, C INTEGER);
INSERT INTO ChodingTable
(SomeDate, A, B, C)
VALUES ('2022-01-01', 'A1', 50, 1),
('2022-01-01', 'A1', 50, 1),
('2022-01-02', 'A1', 20, 2),
('2022-01-02', 'A2', 20, 2);
SELECT SomeDate,GROUP_CONCAT(DISTINCT(A) SEPARATOR '+'),SUM(B),(C)
FROM ChodingTable
WHERE C=1
GROUP BY SomeDate,A
UNION ALL
SELECT SomeDate,GROUP_CONCAT(DISTINCT(A) SEPARATOR '+'),SUM(B),C
FROM ChodingTable
WHERE C=2
GROUP BY SomeDate;

How to create new column and new row based on two tables?

I have two tables:
Table 1
MARKET ATC3 ATC4 PRODUCT BOOLEAN FLAG JOINING COLUMN
A1 B1 B1C1 D1 1 ATC4
A2 B1 B1C2 D2 1 ATC4
A2 B1 B1C3 ATC4
FAMILY A B1 ATC3
Table 2:
PRODUCT ATC3 ATC4 VALUES
D1 B1 B1C1 10
D1 B1 B1C1 20
D2 B1 B1C2 15
D2 B1 B1C2 25
D2 B1 B1C2 10
D3 B1 B1C3 5
My desired output:
PRODUCT ATC3 ATC4 VALUES MARKET VALUES
D1 B1 B1C1 10 A1 10
D1 B1 B1C1 20 A1 20
D2 B1 B1C2 15 A2 15
D2 B1 B1C2 25 A2 25
D2 B1 B1C2 10 A2 10
D3 B1 B1C3 5 A2 5
ALL D1+D2+D3 FAMILY A 85
The idea is, Table 2 has many rows and products but does not have Market. Table 1 helps you find out which product in Table 2 belongs to which Market-based on the Joining column. For example, There are 3 Markets present in Table 1, I want to then assign a new column Market in Table 2 such that all PRODUCTS in Table 2 with the ATC4 code of B1C1 belongs to the Market A1. Why? Because in Table 1, it says that Market A1 should follow the Joining Column of ATC4 - which corresponds to the code B1C1. In Table 1, we also provided a Product column, this is just for our purpose of identifying our own companies product name. Now if you see that for Table 1, there are two rows of Market A2, with different ATC4, this is very normal, because maybe Product D2 and D10 belong to Market A2, but both may contain different ATC4!
There is also one more nuance to it, we have Family A! This is merely a combination of A1+A2, but in my Table 2, there is no such row value that sums up to Family A. So I need to achieve two things:
I want to make a new column Market in Table 2 so that each product is mapped to the market.
I want to create extra rows to account for the Market Family A (A1+A2) and call the product Name "Lovely Family A" or something. The above table 3 provides an expected output.
Since I am new to SQL, I tried to first use CASE Statements, to map slowly one by one, but soon it gets tedious and I wonder if there's some tricks.
My CASE looks like this
,CASE WHEN ATC4 LIKE '%B1C1%' THEN 'A1'
WHEN ATC4 LIKE '%B1C2%' OR ATC4 LIKE '%B1C3%' THEN 'A2' ELSE ATC4 END AS MARKET_NAME
but have yet to figure out how to add the additional row where I can sum up A1+A2.
You seem to want something like this:
with rows as (
select PRODUCT, ATC3, ATC4, VALUES
from table2
union all
select 'ALL D1+D2+D3', ATC3, NULL, SUM(VALUES)
from table2
group by ATC3
)
select r.*, t1.market
from rows r join
table1 t1
on (t1.joining_column = 'ATC3' and t1.atc3 = r.atc3) or
(t1.joining_column = 'ATC4' and t1.atc4 = r.atc4);
I see no reason to repeat the values column. And values is a really bad name for a column because it is a SQL keyword.

Join table A into table B multiple times

I have 2 tables in a mysql db (simplified example data):
Table 1: 3 names (unique)
Table 2: names (only from table 1), 3 dates, info
For each date there are sometimes all 3 names in table 2, sometimes less then 3.
Example for table 2:
d1 n1 info
d1 n2 info
d1 n3 info
d2 n1 info
d2 n3 info
d3 n3 info
Date 1 has got all 3 names, date 2 has got 2 names, date 3 hast got 1 name.
Goal: I need each "date" to have all 3 names. I can already filter 1 date from table 2 and join the tables successfully to have the desired outcome for one date, but how can I "add" all names to each date?
d1 n1 info
d1 n2 info
d1 n3 info
d2 n1 info
d2 n2 (added by the join, "info" is empty)
d2 n3 info
d3 n1 (added by the join, "info" is empty)
d3 n2 (added by the join, "info" is empty)
d3 n3 info
My real data has got much more names and dates, which makes individual joins impractical. It feels like there should be an easy solution to this, but I could not find any.
I also could do this with code ("for each date add missing names") , but I wonder if it can be done with sql
Working on MySQL 10.1.37
Use CROSS JOIN to create all possible combinations of names and dates, followed by a LEFT JOIN:
SELECT name_table.name, datelist.date, info_table.info
FROM name_table
CROSS JOIN (SELECT DISTINCT date FROM info_table) AS datelist
LEFT JOIN info_table ON name_table.name = info_table.name AND datelist.date = info_table.date

Selecting values by last date

I have table with several entries for one point, is it possible to show only last entry for each point?
Example
points | date
A1 2016-02-12
A1 2016-02-15
A1 2016-03-12
B1 2016-01-11
B1 2016-03-15
B1 2015-09-28
C1 2016-01-28
C2 2016-03-03
D1 2015-12-12
D1 2016-01-12
E2 Null
E3 Null
F1 Null
I want to get something like this, without ignoring Null values.
points | date
A1 12.03.2016.
B1 12.03.2016.
C1 03.03.2016.
D1 12.01.2016.
E2 Null
E3 Null
F1 Null
I edited question because I tried group by and it didn't work for me( I forgot to mention before) It showed only entries with date, and I need points with null value as well
Something like this:
A1 12.03.2016.
B1 12.03.2016.
C1 03.03.2016.
D1 12.01.2016.
You can get by using MAX FUNTION
SELECT points,MAX(date)
FROM table_name
GROUP BY points;
if you want to change date format you can use DATE_FORMAT function

Get N number of records from child table for each parent record in a MySQL View

I tried finding answer to this question in SO , but could not find any. Any links will be of great help.
I have a parent table and a child table with one to many relationship between the parent and child table. The child table contains around 1 million records and I want to create a view with 1st 10 records in child table for each parent record.
Example-
Parent_Table - Fields -- ID, Name
ID Name
---- -----
1 A
2 B
3 C
Child_Table - Fields -- ID, ParentID, Date, Data
ID ParentID Date Data
--------------------------
1 1 04/10 A1
2 1 04/11 A2
3 1 04/11 A3
4 1 04/12 A4
5 1 04/12 A5
6 2 04/10 B1
7 2 04/11 B2
8 2 04/12 B3
9 2 04/12 B4
10 2 04/13 B5
11 2 04/13 B6
Now, I want to create a view with 1st 4 records for each parent record sorted by date.
Output Expected
ID ParentID Date Data
--------------------------
1 1 04/10 A1
2 1 04/11 A2
3 1 04/11 A3
4 1 04/12 A4
6 2 04/10 B1
7 2 04/11 B2
8 2 04/12 B3
9 2 04/12 B4
Any links or guide to the solution will be appreciated. Thanks in advance!
In case you need any clarification, please post a comment.
If you need to create a VIEW, you could use something like this:
CREATE VIEW First_Four AS
SELECT c1.*
FROM
Child_Table c1 LEFT JOIN Child_Table c2
ON c1.ParentID = c2.ParentID
AND (STR_TO_DATE(c1.`date`, '%m/%Y')>STR_TO_DATE(c2.`date`, '%m/%Y')
OR (STR_TO_DATE(c1.`date`, '%m/%Y')=STR_TO_DATE(c2.`date`, '%m/%Y')
AND c1.ID>c2.ID)
)
GROUP BY
c1.ID, c1.ParentID, c1.`Date`, c1.Data
HAVING
COUNT(c2.ID)<4
I'm considering the field data as a VARCHAR column, so we need to use STR_TO_DATE, if it is not we can just compare c1.date with c2.date directly.
Please see fiddle here.
I tried this one my computer and it displayed based on your requirements using your own data. I changed some field name though like ID of Child to ChildID, Date to ChildDate, Data to ChildData. Here it is:
SELECT * FROM
(SELECT ParentID, ChildID, ChildDate, ChildData, #ChildRank:= CASE WHEN #Parent <> ParentID THEN 1 ELSE #ChildRank+1 END as ChildRanking, #Parent := ParentID as Parent FROM
(SELECT #ChildRank:=0) CR, (SELECT #Parent:=1) P, (SELECT * FROM Child_Table ORDER BY
ParentID, ChildID) MainTable) AllTable WHERE ChildRanking <=4;
I use only the Child Table only but anyway you could INNER JOIN this with Parent_Table if you like.
A little explanation:
1) ChildRank will starts with Rank 0 (i.e. SELECT #ChildRank:0) but because of #ChildRank+1 it will start with Rank 1
2) When new ParentID (i.e. #Parent<> ParentID) then starts with Rank 1 right away.
3) AllTable is the alias for everything so that you could now reference the ChildRanking field.
If you don't want to display the ChildRanking field then you have to specify the fields you want to dispaly.