This question already has answers here:
MySQL how to fill missing dates in range?
(6 answers)
Closed 4 years ago.
I have a table with information and dates, which have some missing ones, so I want to join that table with a calendar table to fill missing dates and set values in another column in the same row to null. This is an example:
Steps | Date
10 | 2018-04-30
20 | 2018-04-28
And it want to do the following:
Steps | Date
10 | 2018-04-30
null | 2018-04-29
20 | 2018-04-28
This is what I tried (real query, so you can point out if I'm doing something wrong):
SELECT sum(steps), date(from_unixtime(u.in_date)) as stepdate
FROM userdata u
RIGHT JOIN
time_dimension td
ON date(from_unixtime(u.in_date)) = td.db_date
AND user_id = 8
GROUP BY day(from_unixtime(in_date))
ORDER BY stepdate DESC;
I expected this query to do what I wanted, but it doesn't. The table time_dimension and its column db_date have all dates (ranging from 2017-01-01 to 2030-01-01), which is the one I'm trying to join userdata's in_date column (which is in unix_time).
Edit: I checked the following questions in SO:
Join to Calendar Table - 5 Business Days
What's the difference between INNER JOIN, LEFT JOIN, RIGHT JOIN and FULL JOIN?
Edit, regarding the duplicate: That question in particular is using intervals and date_add to compare against their table. I am using a calendar table instead to join them. While similar, I don't think they won't have the same solution.
Solution: Thanks to xQBert, who pointed out the mistake:
PROBLEM: Having the group by be on the userdata table as well as the select, you're basically ignoring the time dimension data. There is no 2018-4-29 date in Userdata right (for user 8) Fix the select & group by to source from time dimension data and problem solved.
So, I changed GROUP BY day(from_unixtime(in_date)) to GROUP BY td.db_date.
You need left join rather than right join or you may also change the position of tables
SELECT sum(steps), date(from_unixtime(td.db_date)) as stepdate
FROM time_dimension td
LEFT JOIN userdata u
ON date(from_unixtime(u.in_date)) = td.db_date
WHERE user_id = 8
GROUP BY date(from_unixtime(td.db_date))
ORDER BY stepdate DESC;
However, this assumes time_dimension table treating as calender table.
Related
This question already has answers here:
How to select and/or delete all but one row of each set of duplicates in a table?
(2 answers)
How can I remove duplicate rows?
(43 answers)
Closed 1 year ago.
I've a flights table that consists of few columns but somebody seem to have ran a migration twice that resulted in creation of same data twice.
Anyway, the flight should only have only data from the following condition: The flight_number and the date.
Basically the table is looking like this at the moment:
flight_number
date
123
2021-09-16
123
2021-09-16
123
2021-09-17
124
2021-09-18
124
2021-09-18
Result I want:
flight_number
date
123
2021-09-16
123
2021-09-17
124
2021-09-18
Basically, keep only one and remove duplicated (if the flight_number is same of the same date).
I'm looking for a DELETE SQL query but couldn't find the one like I am looking for.
What is the query that can help me achieve it?
Thanks!
EDIT: Yes, all the data has a column id that is unique even if the data is same.
You need to identify which rows to keep and which to remove; this can be done as such:
delete ff from
flight ff
inner join (
select flight_number, row_number() over (partition by flight_number order by date) as RN
from flight f
) dups
on ff.flight_number = dups.flight_number
where dups.rn > 1
Basically, this uses Row_Number to create a row identifier based on certain criteria, in this case, for each (partition) Flight_number, create a row number then delete any records where the row_number is > 1.
You will need to change this to use the actual ID column on the join, like this https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=58a4ac7235ea22b557116ad68c8449c3
I have the following situation. I have a table with all info of article. I will like to compare the same column with it self. because I have multiple type of article. Single product and Master product. the only way that I have to differences it, is by SKU. for example.
ID | SKU
1 | 11111
2 | 11112
3 | 11113
4 | 11113-5
5 | 11113-8
6 | 11114
7 | 11115
8 | 11115-1-W
9 | 11115-2
10 | 11116
I only want to list or / and count only the sku that are full unique. follow th example the sku that are unique and no have variant are (ID = 1, 2, 6 and 10) I will want to create a query where if 11113 are again on the column not cout it. so in total I will be 4 unique sku and not "6 (on total)". Please let me know. if this are possible.
Assuming the length of master SKUs are 5 characters, try this:
select a.*
from mytable a
left join mytable b on b.sku like concat(a.sku, '%')
where length(a.sku) = 5
and b.sku is null
This query joins master SKUs to child ones, but filters out successful joins - leaving only solitary master SKUs.
You can do this by grouping and counting the unique rows.
First, we will need to take your table and add a new column, MasterSKU. This will be the first five characters of the SKU column. Once we have the MasterSKU, we can then GROUP BY it. This will bundle together all of the rows having the same MasterSKU. Once we are grouping we get access to aggregate functions like COUNT(). We will use that function to count the number of rows for each MasterSKU. Then, we will filter out any rows that have a COUNT() over 1. That will leave you with only the unique rows remaining.
Take that unique list and LEFT JOIN it back into your original table to grab the IDs.
SELECT ID, A.MasterSKU
FROM (
SELECT
MasterSKU = SUBSTRING(SKU,1,5),
MasterSKUCount = COUNT(*)
FROM MyTable
GROUP BY SUBSTRING(SKU,1,5)
HAVING COUNT(*) = 1
) AS A
LEFT JOIN (
SELECT
ID,
MasterSKU = SUBSTRING(SKU,1,5)
FROM MyTable
) AS B
ON A.MasterSKU = B.MasterSKU
Now one thing I noticed from you example. The original SKU column really looks like three columns in one. We have multiple values being joined with hypens.
11115-1-W
There may be a reason for it, but most likely this violates first normal form and will make the database hard to query. It's part of the reason why such a complicated query is needed. If the SKU column really represents multiple things then we may want to consider breaking it out into MasterSKU, Version, and Color or whatever each hyphen represents.
I have two tables named LocalVSDB and TemporaryVSDB. Both tables have the same columns:
LocalVSDB: msisdn,activateDate
TemporaryVSDB: msisdn,activateDate
But both tables also have duplicate rows for MSIDSN
I need to join these two tables. My intended result looks like this:
MSISDN LocalActivateDate TemporaryActivateDate Datediff
60103820251 2013-12-14 2013-10-05 70
601111000254 2013-12-14 2013-10-05 70
601111000254 2013-12-18 2013-09-10 80
But, since there are duplicate MSIDSNs, I am getting duplicate rows when I join. For example there are 6 rows for certain MSISDN in each table so when I am joining I am getting total 36 rows for that MSISDN.
I am joining using the following query:
SELECT t.msisdn,t.activateDate AS VSDB_Activate_Date,
l.activateDate AS Local_Activate_Date,
DATEDIFF(D,l.activateDate,t.activateDate) AS date_Diff
FROM temporaryVSDB2 t
INNER JOIN LocalVSDB l ON t.msisdn = l.msisdn
WHERE t.activateDate > l.activateDate
Please help me how can I get 6 rows for 6 MSISDN?
Thanks in advance.
The problem is:
where t.activateDate > l.activateDate
That means one row in table one can join to all six rows in table two. You either need to change this to an = or just get a single row from the second table based on certain criteria.
SELECT m.MSIDN, m.ActiveDate, t.ActiveDate, DATEDIFF(DAY, m.ActiveDate, t.ActiveDate) Duration
FROM LocalVSDB m
OUTER APPLY
(
SELECT TOP 1 d.MSIDN, d.ActiveDate
FROM TemporaryVSDB d
WHERE d.ActiveDate > m.ActiveDate
ORDER BY d.ActiveDate
) t
This would find the nearest partner record and duration (the last record will have a null partner though)
You can use your own query adding group by clause provided msidn and activateDate produce unique row.
SELECT t.msisdn,t.activateDate AS VSDB_Activate_Date,
l.activateDate AS Local_Activate_Date,
DATEDIFF(D,l.activateDate,t.activateDate) AS date_Diff
FROM temporaryVSDB2 t INNER JOIN LocalVSDB l ON t.msisdn = l.msisdn
WHERE t.activateDate > l.activateDate
group by t.msisdn, t.activateDate
I need to get a list of months that are not located inside the database.
Example:
Table members
ID | Member's code | Member since
1 | 555-12 | 2012-11-22
Table membership
ID | Code | Paid
1 | 555-12 | 2013-1-1
2 | 555-12 | 2013-3-12
3 | 555-12 | 2013-5-1
Let's say that today is : 2013-11-17
I need to get output like this:
Member's code | Debt ( Months )
555-12 | 11-2012
555-12 | 12-2012
555-12 | 2-2013
555-12 | 4-2013
Is this possible to do with a SQL? Do I need to have a stored procedure where I will pass Member's code?
My idea is to use a number table, that contains just numbers from 0 to 100 or more:
CREATE TABLE numbers (
n INT
);
INSERT INTO numbers (n)
VALUES
(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15)...;
Then you can use a query like this:
SELECT
m.ID,
m.Code,
DATE_FORMAT(m.Member_since + INTERVAL num.n MONTH, '%m-%Y') As Debt_Month_Year
FROM
members m INNER JOIN numbers num
ON TIMESTAMPDIFF(MONTH, m.Member_since, LAST_DAY(CURDATE()))>=num.n
LEFT JOIN membership ms
ON
m.Code = ms.Code
AND
LAST_DAY(ms.Paid)=LAST_DAY(m.Member_since + INTERVAL num.n MONTH)
WHERE
ms.id IS NULL
-- and if you wish, add the following line:
AND m.Code = '555-12'
Please see fiddle here.
select code,
left(since,7) as debt
from user where code='555-12'
union all
select code,
left(date_add(paid, interval -1 MONTH),7) as debt
from paid where code='555-12'
Fiddle
http://sqlfiddle.com/#!2/3d988/1
If you can guarantee that you will only have a consistent one month gap between the entries for each user, then I think timus's solution should work.
If not, I think you'll need to have some sort of date table to determine what values you don't have in the database.
Here's a SQL Fiddle demonstrating what I'm talking about. I intentionally removed one of the rows from your paid table to test skipping more than one month. Basically it gets the month from the member table (the first entry, I guess), and I union that to a second query getting the months that are not in the paid table.
SQL Fiddle
select
left (mbr_since,7) as MNTH
from
user
union
select
mnth
from
months
left outer join paid
on months.mnth = left(paid,7)
where
paid.code is null
The months table is just a row for each month (2013-01, 2013-02, etc). The second query joins to that, and then filters out rows where there is a match in the paid table. Hopefully my explanation is making sense...
When I make this sql statement I get 6 of the same record returned. So if I expect to get 2 records returned, I get six of each record back so that is 12 in total.
SELECT
ce2.*
FROM customerentry ce, customerentrytrace cet, customerentry ce2
WHERE ce.accountid = 1
AND ce.companyid = 1
AND ce.accountid=cet.accountid
AND ce.accountid=ce2.accountid
AND ce.companyid=cet.companyid
AND ce.companyid=ce2.companyid
AND cet.documentno = '2012Faktura1'
AND cet.documenttype = 1
AND ce2.documentno = cet.offsetdocumentno
AND ce2.documenttype = cet.offsetdocumenttype
ORDER BY created;
I know that I can solve it by adding distinct, but I would like to know why I get 6 of the same record returned. Anyone who can help me?
Since we have no idea about your table structure probably there are some columns that are related 1 to n items and you haven't handled them in the WHERE section of your query.
As an extra measure you can focus on your data needs and add a GROUP BY section before your ORDER section.
You are using an INNER JOIN, so for example there are two entries in table cet matching your where clause for combining table ce and cet, giving you 2 entries/entry of table ce.
Thinking this further you can see that if there are 3 entries in table ce2 matching the where clause for combining table cet and ce2 you get 3 entries/entry of table cet.
Which makes 6 entries per entry of table ce in total, giving you 12 entries in total even if you have only 2 entries in table ce.
So think again about what join could be the right for your desired solution.
Here a link for some more explanation: Short explanation of joins
Problem might be because you have not properly joined tables. Please read about JOIN
SELECT ce2.*
FROM customerentry ce INNER JOIN customerentrytrace cet ON ce.accountid=cet.accountid AND ce.companyid=cet.companyid,
INNER JOIN customerentry ce2 ON ce.accountid=ce2.accountid AND ce.companyid=ce2.companyid AND ce2.documentno = cet.offsetdocumentno AND ce2.documenttype = cet.offsetdocumenttype
WHERE ce.accountid = 1
AND ce.companyid = 1
AND cet.documentno = '2012Faktura1'
AND cet.documenttype = 1
ORDER BY created;