Sisense: Self table join for historical data reporting - mysql

Let me start with an example. Suppose I have the following snippet of a slowly changing dimension table containing info about customers:
key id name country valid_from valid_to version
1 abcxyz John Switzerland 2012/01/01 2014/01/01 1
20 abcxyz John Germany 2014/01/01 2017/01/01 2
...
As you can see, every change in a customer's info is recorded as a new entry in the table with the same id, but the version incremented by 1 (key field is primary key of table)
This table is then imported in an Sisense ElastiCube, and then I have access to it from the Dashboard web app.
My question is: How can I create a widget which shows me all customers that moved from country A to country B? (if A were Switzerland and B were Germany, John would show up in the report)

Assuming that (id, version) is unique and that there are no gaps in the version sequence, this query would give you the customers who moved from one country to another.
Please note that it does not give you the most recent version of the customer record, but rather the version of the record when the change occurred. John could be living in Italy in version 3, but the query would still give you the Germany record.
select a.key as moved_from_key
,b.key as moved_to_key
from customers a
join customers b on(
b.id = a.id
and b.version = a.version + 1 -- The version following A
)
where a.country = 'Switzerland'
and b.country = 'Germany';

Related

Efficiently get latest appointment for every person sorted by oldest first

I already asked this question earlier but forgot a few (important) details or got them wrong.
My table in MySQL 8.0.29 looks like this
UserID
Appointment
Description
Bob
2022-06-01
Cleaning
Bob
2022-06-03
Toothache
John
2022-06-02
Braces
I'm trying to get the latest appointment for every person sorted by oldest first.
The query should return
UserID
Appointment
Description
John
2022-06-02
Braces
Bob
2022-06-03
Toothache
Using one of the previous answers I get
SELECT Name, Appointment, Description
FROM (
SELECT Name, Appointment, Description, ROW_NUMBER() OVER(PARTITION BY Name ORDER BY Appointment DESC) rn) t1
WHERE rn = 1
The problem is the database currently has 3 million rows and it'll continue to grow so this query ends up being pretty slow.
My plan is to consume the data in chunks so I'd prefer the query having "pagination". Something like a LIMIT 0, 5000 to get 5000 records at a time.
I'm open to even re-architecting the database if it comes to that.
For now i've resorted to creating a new table that just keeps the latest appointment for each user.
You are halfway there. Use that query as a 'derived table' instead of making it permanent:
SELECT b.*
FROM ( SELECT user_id, MAX(appointment) AS last_date)
FROM tbl
GROUP BY user_id ) AS x
JOIN tbl AS b ON b.user_id = x.user_id
AND b.appointment = x.last_date
And be sure to have INDEX(user_id, appointment)
I would be interested to see if this and the "OVER" approach both give the same results and which is faster.

mysql update rows of column in second table from column in first table where another column in first table matches column in second table

My title may be a little confusing, but this is basically what I want to do:
I have two tables:
Table 1 = Site
Columns: SiteID SiteName Address
1 Germany 123 A Street
2 Poland 234 B Street
3 France 354 F Street
4 England 643 C Street
5 Russia 968 G Street
Table 2 = Site_New
Columns: SiteID SiteName Address
1 Germany
2 France
3 Russia
I wan't to update the Address column in table 2 with the Address in table 1 where SiteName in table 2 = SiteName in table 1. As you can see there are sites in table 1 that are not in table 2, so I do not care about copying those addresses to table 2.
I was trying this code:
update Site_New set Address = (select Site.Address from Site where Site_New.SiteName=Site.SiteName)
but I was getting error code 1242: "Subquery returns more than 1 row."
Any idea on how this can be done?
You are better off using update/join syntax:
update Site_New sn join
Site s
on sn.SiteName = s.SiteName
set sn.Address = s.Address;
However, based on your sample data, your correlated subquery should not cause such an error.
Perhaps the join should be on SiteId rather than SiteName:
update Site_New sn join
Site s
on sn.SiteId = s.SiteId
set sn.Address = s.Address;
you need to do a select with your update like so
UPDATE site_new sn,
( SELECT
sn1.address as _address, sn1.sitename as _sitename
FROM site_new sn1
JOIN site s on s.sitename = sn1.sitename
) t
SET sn.address = t._address
WHERE sn.sitename = t._sitename

for loop functionality in mysql query

this is MYSQL query question
First, let say we have
[ALL_MENU]
name
-----
pasta
pizza
and people ordered
ordered
customer name status
john pasta delivered
kim pasta delivered
john pizza delivered
john pasta delivered
I want to go through the ordered table and find anyone who ordered all the menu
In this example, kim only ordered pasta so it should not be included
but john ordered both pasta and pizza, so it should be included on the result.
is There any type of query to do the 'For loop' ability?
thank you
(ps, right now I only allow to use
some basic function command from mysql
group by , create view, exists , all, not exist , unique such and such )
=========================================================================
Ok, From the answer
the count number of all menu thing is work when (customer,name) are the primary key
But what if i added the data column, and primary key as (customer,name,data)
customer name status date
john pasta delivered 3/4
kim pasta delivered 3/5
john pasta delivered 3/5
this is the new column i made
john ordered the only pasta with 2 different date,
but johns number of order count as 2.
so count method will not work for this
How do we fix this problem?
The functionality of a for loop is a cursor. (FYI, most DBMS have such a construct.)
But never use a cursor when a plain old query will do just fine.
Here is one possible solution:
SELECT customer
FROM ordered
GROUP BY customer
HAVING COUNT(DISTINCT name) = (SELECT COUNT(*) FROM all_menu)
(This assumes that all names in ordered are found in all_menus, e.g. there is foreign key. If not, you'll have to add JOIN all_menu ON ordered.name = all_menu.name in the FROM clause.)
EDIT: "Simple"(!) commands only:
SELECT customer
FROM ordered o1
WHERE NOT EXISTS (
SELECT * FROM all_menu
WHERE name NOT IN (SELECT name FROM ordered o2 WHERE o1.customer = o2.customer)
)

MySQL: Get results given a condition

I have a table that looks like this:
target_id || country_code
5-----------||-------US----
5-----------||-------CA---
2----------||-------FR----
3----------||-------SP----
3----------||-------FR----
And another table that looks like this:
target_id || region_name
5-----------||---North America
2-----------||-----France------
3-----------||-----Some Europe
As you can see, table 2 contains locations and target_ids, while table 1 contains where these locations are targeted. In the case of North America, it is targeted to 5, which belongs to Canada and US. France, on the other hand has a target_id of 2, and Some Europe a target_id of 3, which contains France again and Spain.
What I would like to do via MySQL, is to get a table of target_id, country_code, country_name but only for countries. This means, only to the target_ids of table 1 that are in only one row (for example, we know that FR is a country because number 2 is only in FR, and we know that 3 represents a region because it has both Spain and France associated). Is this possible to do via MySQL or will I need two queries and PHP in the middle?
Thanks!
SELECT t1.target_id, t1.country_code, t2.region_name
FROM table1 t1
JOIN table t2
ON t1.target_id = t2.target_id
WHERE (SELECT COUNT(*) FROM table1 t3 WHERE t3.target_id = t1.target_id) = 1
table1 is the one with the country codes, table2 is the one with the the region names.

MySQL relational database query, correct terminology?

I think my issue with databases stems from not knowing the correct terminology to help me find an answer myself so I'll explain a generic version of what I'm doing and hopefully you can point some tutorials my way or give me some terms to check into.
Let's use an example of an employee directory.
Each employee can have multiple locations, multiple job duties which pull from a separate table. Example tables & some data, let's just focus on the multiple locations.
employees
Main employee data
- id (ex: 400)
- first (ex: John)
- last (ex: Doe)
locations
Unique list of locations
- id (ex: 3000)
- title (ex: FakeCo, LLC)
map_employees_locations
Tie ANY number of locations to an employee
- id
- employee_id (ex: 400)
- location_id (ex: 3000)
I'm struggling with the logic of how a single query would return something like this:
John
Doe
FakeCo, LLC
AnotherCo, LLC
It seems I would have to run a query to get the employee data, then within a nested query, grab locations associated with the employee id, etc... If there was only one location per employee, it would be a simple join, I just don't know how to handle the multiples.
Let me know if I'm way off, I'm just struggling.
You would join all of the tables together like this
select e.id,e.first,e.last,l.id,l.title
from employees e
inner join map_employees_locations el
on el.employee_id = e.id
inner join locations l
on el.location_id = l.id
where e.first = 'John'
AND e.last = 'Doe'
This would return data like this:
e.id e.first e.last l.id l.title
------------------------------------------------
1 John Doe 1 FakeCo, LLC
1 John Doe 2 AnotherCo, LLC
If you want only one line per employee you should maybe use group concat
select id, e.last, e.first
group_concat(l.title separator ',' ) as locations
from employee e
join location l on l.employee_id = e.id
group by e.id
Not sure about the syntax cos i'm more aware of postgres but this should do the job.