MySQL Join 2 columns from table y into rows of table x - mysql

Trying to wrap my mind around how to write this SQL query.
Table X has 3 Columns: Year, ID, Value and looks like so
Year | ID | Value
2013 101 10000
2014 101 11000
2015 101 12000
2013 102 7000
2014 102 8000
2015 102 9000
And table Y has 3 Columns: ID, Curr_Year_Val, Next_Year_Val and looks like this
ID | Curr_Year_Val | Next_Year_Val
101 13000 14000
102 6000 5000
I would like to write a select statement to join these two tables together, but maintain the layout of Table X, like so:
Year | ID | Value
2013 101 10000
2014 101 11000
2015 101 12000
Curr_Year_Val 101 13000
Next_Year_Val 101 14000
Is there a way to achieve this result? I've figured out how to just do a left join to add the columns from table y to table x, but would rather have the columns from table y unpivoted to the rows of table x. Thanks much in advance - this seems like it should be so easy, I've been googling for hours but I'm probably not using the proper terminology for what I'm trying to do in my searches.
Thanks!

Sounds like you should use union all:
select year, id, value from x
union all
select 'curr_year_val', id, curr_year_val from y
union all
select 'next_year_val', id, next_year_val from y
order by 2, 1
SQL Fiddle Demo
BTW, other databases would require you to have the same data types for all columns when using union. This works though with mysql.

Uee union
select year, id, value
from tableX
where id ='101'
union
select 'curr_year_val', id, curr_year_val
from tableY
where id ='101'
union
select 'next_year_val', id, next_year_val
from tableY
where id ='101'

Related

How to assign to each row a number of times a value appears in the whole table?

I'm trying to run an SQL query on Vertica but I can't find a way to get the results I need.
Let's say I have a table showing:
productID
campaignID (ID of the sales campaign)
calendarYearWeek (calendar week when the campaign was active [usually they're active for 5 days)
countryOrigin (in which country was the product sold, as it's international sales)
valueLocal (price in local currency)
What I need to do is to find products sold in different countries and compare their prices between markets.
Sometimes the campaigns are available only in one country, sometimes in more, so to avoid having hundreds of thousands of unnecessary rows that I can't compare to others, I want to distill only those products that were available in more than 1 countryOrigin.
What's important - a product can be available in different campaigns with a different price.
That's why in my SELECT statement I added a new column:
calendarYearWeek||productID||campaignID AS uniqueItem - that way I know that I'm checking the price only for a specific product in a specific campaign during a specific week of year.
The table is also joined with another table to get exchange rates etc., so it's also GROUPed BY, so in each row I have a price and average exchange rate for a given uniqueItem in a specific country.
If I run this query, it works but even just for this year it gives me several million results, most of which I don't need because these are products sold only in one country and I need to compare prices across different markets.
So what I thought I need is to assign to each row a number of times a uniqueItem value appears in the whole table. If it's 1 - then the product is sold only in one country and I don't have to care about it. If it's 2 or 3 - this is what I need. Then I can filter out the unnecessary results in the WHERE clause ( > 1) and I can work on a smaller, better data set.
I tried different combinations of COUNT, I tried row_number + OVER(PARTITION BY) (works only partially, as when a product is available in 2 or more countries it counts the rows, but still I cannot filter out "1" because then I'll lose the "first" country on the list). I thought about MATCH_RECOGNIZED, but I've never used it before and I think it's not available in Vertica.
Sorry if it's messy, but I'm not really advanced in SQL and English is not my native language.
Do you have any ideas how to get only the data I need?
What I have now is:
SELECT
a.originCountry,
a.calendarYearWeek,
a.productID,
a.campaignId,
a.valueLocal,
ROUND(AVG(b.exchange_rate),4),
a.calendarYearWeek||a.productID||a.campaignID AS uniqueItem
FROM table1 a
LEFT JOIN table2 b
ON a.reportDate = b.reportDate
AND a.originCountry = b.originCountry
WHERE a.originCountry IN ('ES', 'DE', 'FR')
GROUP BY 3, 4, 7, 1, 5, 2
ORDER BY 3, 4, 1
----------
I need some sample data - so I make up a few rows.
You need to find the identifying grouping columns of those combinations that occur more than once in a sub select or a common table expression, to join with table1.
You need to formulate the average as an OLAP function if you want the country back in the report.
WITH
-- input, don't use in final query ..
table1(originCountry,calendarYearWeek,productID,campaignId,valuelocal,reportDate) AS (
SELECT 'ES',202203,43,142,100.50, DATE '2022-01-19'
UNION ALL SELECT 'DE',202203,43,142,135.00, DATE '2022-01-19'
UNION ALL SELECT 'FR',202203,43,142, 98.75, DATE '2022-01-19'
UNION ALL SELECT 'ES',202203,44,147,198.75, DATE '2022-01-19'
UNION ALL SELECT 'DE',202203,44,147,205.00, DATE '2022-01-19'
UNION ALL SELECT 'FR',202203,44,147,198.75, DATE '2022-01-19'
UNION ALL SELECT 'es',202203,49,150, 1.25, DATE '2022-01-19'
)
,
table2(originCountry,reportDate,exchange_rate) AS (
SELECT 'ES',DATE '2022-01-19', 1
UNION ALL SELECT 'DE',DATE '2022-01-19', 1
UNION ALL SELECT 'FR',DATE '2022-01-19', 1
)
-- end of input; real query starts here, replace following comma with "WITH" ..
,
-- you need the unique ident grouping values to join with ..
selgrp AS (
SELECT
a.calendarYearWeek
, a.productID
, a.campaignId
FROM table1 a
GROUP BY
a.calendarYearWeek
, a.productID
, a.campaignId
HAVING COUNT(*) > 1
-- chk calendarYearWeek | productID | campaignId
-- chk ------------------+--------+--------
-- chk 202203 | 43 | 142
-- chk 202203 | 44 | 147
)
SELECT
a.originCountry
, a.calendarYearWeek
, a.productID
, a.campaignId
, a.valueLocal
, AVG(b.exchange_rate) OVER w::NUMERIC(9,4) AS avg_exch_rate
-- a.calendarYearWeek||a.productID||a.campaignID AS uniqueItem
FROM table1 a
JOIN selgrp USING(calendarYearWeek,productID,campaignId)
LEFT JOIN table2 b
ON a.reportDate = b.reportDate
AND a.originCountry = b.originCountry
WHERE UPPER(a.originCountry) IN ('ES', 'DE', 'FR')
WINDOW w AS (PARTITION BY a.calendarYearWeek,a.productID,a.campaignID)
ORDER BY 3, 4, 1
-- out originCountry | calendarYearWeek | productID | campaignId | valueLocal | avg_exch_rate
-- out ---------------+------------------+-----------+------------+------------+---------------
-- out DE | 202203 | 43 | 142 | 135.00 | 1.0000
-- out ES | 202203 | 43 | 142 | 100.50 | 1.0000
-- out FR | 202203 | 43 | 142 | 98.75 | 1.0000
-- out DE | 202203 | 44 | 147 | 205.00 | 1.0000
-- out ES | 202203 | 44 | 147 | 198.75 | 1.0000
-- out FR | 202203 | 44 | 147 | 198.75 | 1.0000

Count ID in sql query

I have the following data,
id emp_id csa_taken
1 100 2
2 100 2
3 100 0
4 100 2
5 101 2
6 101 2
7 101 0
8 101 0
I expect a result with count where csa_taken=2 for individual employee.
expected result:
emp_id count_csa_taken
100 3
101 2
I have tried the following query with a failed attempt.
Select count(employee_id) From $employeeCSA where csa_taken=2
Please suggest as I am new to sql.
If I understand you correctly you like to count all employees with a cas_taken of two. As there are multiple entries for the csa_taken for one employee you need to group them.
E.g.:
SELECT COUNT(*) FROM $employeeCSA WHERE csa_taken = 2 GROUP_BY employee_id
Please note that COUNT(*) counts the rows (not the fields).
You also need group by. Try like:
Select count(employee_id),emp_id From $employeeCSA where csa_taken=2
group by emp_id
If i understand correctly, then you can try this:
SELECT emp_id,COUNT(emp_id) from dbo.Sample WHERE csa_token = 2 GROUP BY emp_id

Mysql comparison between two datasets

I'm having troubles with mysql, I guess I'm missing some special functions to solve my problem.
I have a table, like this:
id - user_id - user_property
1 - 45 - 9986
2 - 45 - 9564
3 - 45 - 9225
4 - 45 - 9824
5 - 45 - 9711
6 - 83 - 9711
7 - 83 - 9924
8 - 83 - 9986
9 - 12 - 9933
10 - 12 - 9993
11 - 72 - 9189
12 - 72 - 9711
13 - 72 - 9225
14 - 72 - 9824
user_id+user_property is unique key
and I have a list of properties, like "9711","9225","9824". I'm trying to get a list of users having ALL those properties, in the most performant way possible. I've tried many ways, like doing 3 single queries and counting results like this
select count(distinct user_id) as tot from
( select user_id from mytable where user_property = 9711
union select user_id from mytable where user_property = 9225
union select user_id from mytable where user_property = 9824) as tmp
having tot = 3
another guess was to merge user properties per-user and searching wanted properties with the function FIND_IN_SET(element, set of elements obtained with GROUP_CONCAT(user_properties separator ','))
the problem is, mytable is really huge, I already need to select data from this and another table joined by user_id (and elaborate results another time after) and I'm guessing there is some better way to do that in terms of performances. any suggestions?
Thanks in advance
What about something like this?
SELECT COUNT(user_id) FROM (
SELECT user_id
FROM mytable
WHERE user_property IN (9711,9225,9824)
GROUP BY user_id
HAVING COUNT(*) >= 3
) users_with_all_properties
You can combine SUM with IN CLAUSE
SELECT COUNT(*)
FROM
(SELECT
user_id,SUM(user_property in (9711, 9225,9824)) sumprop
FROM mytable
GROUP BY user_id
Having sumprop = 3) userhasprop
| COUNT(*) |
| -------: |
| 2 |
db<>fiddle here

Mysql query to sum rows and columns based on column criteria

Please help me with writing a query for the following condition. I have a table which I have listed below
ID Wt1 Wt1_Type Wt2 Wt2_Type Wt3 Wt3_Type Wt4 Wt4_Type
--------------------------------------------------------------
1 200 1 220 1 300 2 400 3
2 100 4 150 3 100 5 120 1
3 100 3 110 1 200 5 100 4
I want a query to sum all the the weights (wt1, wt2, wt3, wt4) grouped on the weight type (wt1_type, wt2_type, wt3_type, wt4_type).
The output should look like
Wt_type Total
1 650
2 300
3 650
4 200
5 300
Can someone please help me draft a mysql query to get this result ?
Thanks
You can try below - using union all and subquery
select Wt_Type,sum(Wt) as total from
(
select Wt1_Type as Wt_Type,Wt1 as Wt from tablename
union all
select Wt2_Type ,Wt2 from tablename
union all
select Wt3_Type ,Wt3 from tablename
union all
select Wt4_Type ,Wt4 from tablename
)A group by Wt_Type
Rather than giving the answer by #fa06, which should work for you, I am going to suggest using a better table design. Here is how you should be storing your data:
ID Type Wt
-------------
1 1 200
2 4 100
3 3 100
4 1 220
5 3 150
6 1 110
7 2 300
8 5 100
9 5 200
10 3 400
11 1 120
12 4 100
Note that there is a single column which stores the type and a single column for that type's weight. Now your expected output just requires a very simple query:
SELECT Type, SUM(Wt) AS Total
FROM yourTableUpdated
GROUP BY Type;
Databases are really good at performing operations across rows, much less so across columns.
Use this it should be work
select Wt_Type,sum(Wt) as total from ( select Wt1_Type as Wt_Type,Wt1 as Wt from tablename union all select Wt2_Type ,Wt2 from tablename union all select Wt3_Type ,Wt3 from tablename union all select Wt4_Type ,Wt4 from tablename )A group by Wt_Type

MySQL Group by week num w/ multiple date column

I have a table with columns similar to below , but with about 30 date columns and 500+ records
id | forcast_date | actual_date
1 10/01/2013 12/01/2013
2 03/01/2013 06/01/2013
3 05/01/2013 05/01/2013
4 10/01/2013 09/01/2013
and what I need to do is get a query with output similar to
week_no | count_forcast | count_actual
1 4 6
2 5 7
3 2 1
etc
My query is
SELECT weekofyear(forcast_date) as week_num,
COUNT(forcast_date) AS count_forcast ,
COUNT(actual_date) AS count_actual
FROM
table
GROUP BY
week_num
but what I am getting is the forcast_date counts repeated in each column, i.e.
week_no | count_forcast | count_actual
1 4 4
2 5 5
3 2 2
Can any one please tell me the best way to formulate the query to get what I need??
Thanks
try:
SELECT weekofyear(forcast_date) AS week_forcast,
COUNT(forcast_date) AS count_forcast, t2.count_actual
FROM
t t1 LEFT JOIN (
SELECT weekofyear(actual_date) AS week_actual,
COUNT(forcast_date) AS count_actual
FROM t
GROUP BY weekOfYear(actual_date)
) AS t2 ON weekofyear(forcast_date)=week_actual
GROUP BY
weekofyear(forcast_date), t2.count_actual
sqlFiddle
You have to write about 30 (your date columns) left join, and the requirement is that your first date column shouldn'd have empty week (with a count of 0) or the joins will miss.
Try:
SELECT WeekInYear, ForecastCount, ActualCount
FROM ( SELECT A.WeekInYear, A.ForecastCount, B.ActualCount FROM (
SELECT weekofyear(forecast_date) as WeekInYear,
COUNT(forecast_date) as ForecastCount, 0 as ActualCount
FROM TableWeeks
GROUP BY weekofyear(forecast_date)
) A
INNER JOIN
( SELECT * FROM
(
SELECT weekofyear(forecast_date) as WeekInYear,
0 as ForecastCount, COUNT(actual_date) as ActualCount
FROM TableWeeks
GROUP BY weekofyear(actual_date)
) ActualTable ) B
ON A.WeekInYear = B.WeekInYear)
AllTable
GROUP BY WeekInYear;
Here's my Fiddle Demo
Just in case someone else comes along with the same question:
Instead of trying to use some amazing query, I ended up creating an array of date_columns_names and a loop in the program that was calling this query, and for each date_column_name, performing teh asme query. It is a bit slower, but it does work