Need Help on .... Select Where Date NOT BETWEEN - mysql

Hope someone can tell ..
Table A Table E
Id | Date Id | Start_date | End_date
1 2012-12-10 1 2012-12-09 2012-12-10
2 2012-12-11 2 2012-12-12 2012-12-14
The Result that I'm hoping ..
2012-12-11
This is the code that I think might work to select date from Table A that not in Table E ranga date...
SELECT * FROM `A`
WHERE `A`.`DATE` NOT BETWEEN (SELECT `E`.`DATE_START` FROM `E`) AND (SELECT `E`.`DATE_END`
FROM `E`);
but unfortunately not, the subquery return more than 1 row.
I wonder how??
thanks

You wonder how the subquery returned more than one row? That's because there's more than one row in the table matching your query.
If you want one row, you'll need to limit the query a little more, such as with:
select `e`.`date_start` from `e` where `e`.`id` = 1
If you want all dates in A that are not contained in any date range in E, one way to do it is to get a list of the A dates that are contained within a range, and then get a list of dates from A that aren't in that list.
Something like:
select date
from a
where date not in (
select a.date
from a, e
where a.date between e.start_date and e.end_date
)
Putting this through the excellent phpMyAdmin demo site as:
create table a (id int, d date);
create table e (id int, sd date, ed date);
insert into a (id, d) values (1, '2012-12-10');
insert into a (id, d) values (2, '2012-12-11');
insert into e (id, sd, ed) values (3, '2012-12-09', '2012-12-10');
insert into e (id, sd, ed) values (4, '2012-12-12', '2012-12-14');
select d from a where d not in (
select a.d from a, e where a.d between e.sd and e.ed
);
results in the output:
2012-12-11
as desired.

To get all records from A that are not inside any of the date ranges in E, get the records that are within the date ranges, and select the ones not in that result:
select *
from A
where Id not in (
select A.Id
from A
inner join E on A.Date between E.Start_date and E.End_date
)

If the Id in table A is the same as the Id in table E :
SELECT *
FROM A, E
WHERE A.Id = E.Id
AND A.Date NOT BETWEEN E.Start_Date AND E.End_Date

What you're looking for here is the set of records in A where there does not exist a record in B for which the date in A is between the begin and end dates in B.
Therefore I'd suggest that you structure the query in that way.
Something like ...
Select ...
From table_A
Where not exists (
Select null
From table_b
Where ...)
Depending on the join cardinality of the tables and their sizes you may find that this performs better than the "find the rows that are not in the set for which a John exists" method, aside from it being a more intuitive match to your logic.

Related

Mysql: How to join a query to find results from another table

I have two tables:
TABLE A
Unique_id
id
price
1
1
10.50
2
3
14.70
3
1
12.44
TABLE B
Unique_id
Date
Category
Store
Cost
1
2022/03/12
Shoes
A
13.24
2
2022/04/15
Hats
A
15.24
3
2021/11/03
Shoes
B
22.31
4
2000/12/14
Shoes
A
15.33
I need to filter TABLE A on a known id to get the Unique_id and average price to join to Table B.
Using this information I need to know which stores this item was sold in.
I then need to create a results table displaying the stores and the amount of days sales were recorded in the stores - regardless of whether the sales are associated with the id and the average cost.
To put it more simply I can break down the task into 2 separate commands:
SELECT AVG(price)
FROM table_a
WHERE id = 1
GROUP BY unique_id;
SELECT store, COUNT(date), AVG(cost)
FROM table_b
WHERE category = 'Shoes'
GROUP BY store;
The unique_id should inform the join but when I join the tables it messes up my COUNT function and only counts the days in which the id is connected - not the total store sales days.
The results should look something like this:
Store
AVG price
COUNT days
AVG cost
A
10.50.
3
14.60.
B
12.44
1.
22.31.
I wwas hard to grasp, what you wanted, but after some thinking and your clarification, it can be solved as the code shows
CREATE TABLE TableA
(`Unique_id` int, `id` int, `price` DECIMAL(10,2))
;
INSERT INTO TableA
(`Unique_id`, `id`, `price`)
VALUES
(1, 1, 10.50),
(2, 3, 14.70),
(3, 1, 12.44)
;
CREATE TABLE TableB
(`Unique_id` int, `Date` datetime, `Category` varchar(5), `Store` varchar(1), `Cost` DECIMAL(10,2))
;
INSERT INTO TableB
(`Unique_id`, `Date`, `Category`, `Store`, `Cost`)
VALUES
(1, '2022-03-12 01:00:00', 'Shoes', 'A', 13.24),
(2, '2022-04-15 02:00:00', 'Hats', 'A', 15.24),
(3, '2021-11-03 01:00:00', 'Shoes', 'B', 22.31),
(4, '2000-12-14 01:00:00', 'Shoes', 'A', 15.33)
SELECT
B.`Store`
, AVG(A.`price`) price
, (SELECT COUNT(*) FROM TableB WHERE `Store` = B.`Store` ) count_
, (SELECT AVG(
`cost`) FROM TableB WHERE `Store` = B.`Store` ) price
FROM TableA A
JOIN TableB B ON A.`Unique_id` = B.`Unique_id`
WHERE B.`Category` = 'Shoes'
GROUP BY B.`Store`
Store | price | count_ | price
:---- | --------: | -----: | --------:
A | 10.500000 | 3 | 14.603333
B | 12.440000 | 1 | 22.310000
db<>fiddle here
This should be the query you are after. Mainly you simply join the rows using an outer join, because not every table_b row has a match in table_a.
Then, the only hindrance is that you only want to consider shoes in your average price. For this to happen you use conditional aggregation (a CASE expression inside the aggregation function).
select
b.store,
avg(case when b.category = 'Shoes' then a.price end) as avg_shoe_price,
count(b.unique_id) as count_b_rows,
avg(b.cost) as avg_cost
from table_b b
left outer join table_a a on a.unique_id = b.unique_id
group by b.store
order by b.store;
I must admit, it took me ages to understand what you want and where these numbers result from. The main reason for this is that you have WHERE table_a.id = 1 in your query, but this must not be applied to get the result you are showing. Next time please look to it that your description, queries and sample data match.
(And then, I think that names like table_a, table_b and unique_id don't help understanding this. If table_a were called prices instead and table_b costs and unique_id were called cost_id then, I wouldn't have had to wonder how the tables are related (by id? by unique id?) and wouldn't have had to look again and again which table the cost resides in, which table has a price and which table is the outer joined one while looking at the problem, the requested result and while writing my query.)

MySQL - Finding how much duplicates are inside the same table given

Considering I have the following two sets of rows (same type) in a WHERE clause:
A B
1 1
2 2
3 4
I need to find how many A is in B
For example, for the given table above, it would be 66% since 2 out of 3 numbers are in B
Another example:
A B
1 1
2 2
3 4
5
3
Would give 100% since all of the numbers in A are in B
Here is what I tried myself: (Doesn't work on all test cases..)
DROP PROCEDURE IF EXISTS getProductsByDate;
DELIMITER //
CREATE PROCEDURE getProductsByDate (IN d_given date)
BEGIN
SELECT
Product,
COUNT(*) AS 'total Number',
(SELECT
(SELECT COUNT(DISTINCT Part) FROM products WHERE Product=B.Product) - COUNT(*)
FROM
products AS b2
WHERE
b2.SOP < B.SOP AND b2.Part != B.Part) AS 'New Parts',
CONCAT(round((SELECT
(SELECT COUNT(DISTINCT Part) FROM products WHERE Product=B.Product) - COUNT(*)
FROM
products AS b2
WHERE
b2.SOP < B.SOP AND b2.Part != B.Part)/count(DISTINCT part)*100, 0), '%') as 'Share New'
FROM
products AS B
WHERE
b.SOP < d_given
GROUP BY Product;
END//
DELIMITER ;
CALL getProductsByDate (date("2018-01-01"));
Thanks.
Naming your tables TA and TB respectively you could try something like this (test made on MSSQL and Mysql at moment)
SELECT ROUND(SUM(PERC) ,4)AS PERC_TOT
FROM (
SELECT DISTINCT TA.ID , 1.00/ (SELECT COUNT(DISTINCT ID) FROM TA) AS PERC
FROM TA
WHERE EXISTS ( SELECT DISTINCT ID FROM TB WHERE TB.ID=TA.ID)
) C;
Output with your first sample data set:
PERC_TOT
0,6667
Output with your second sample data set:
PERC_TOT
1,0000
Update (I made the original for two tables, as I was thinking at solution). This is for one single table (is almost the same than the former query): (I used ID1 for column A and ID2 for column B)
SELECT ROUND(SUM(PERC) ,4)AS PERC_TOT
FROM (
SELECT DISTINCT TA.ID1 , 1.00/ (SELECT COUNT(DISTINCT ID1) FROM TA) AS PERC
FROM TA
WHERE EXISTS ( SELECT DISTINCT ID2 FROM TA AS TB WHERE TB.ID2=TA.ID1)
) C;

SQL Find date range gaps in Table

Good day.
I seem to be struggling with what seems like a simple problem.
I have a table that has a value connected to a date (Monthly) for a finite number of ID's
ie. Table1
ID | Date ---| Value
01 | 2015-01 | val1
01 | 2015-02 | val2
02 | 2015-01 | val1
02 | 2015-03 | val2
So ID: 02 does not have a value for date 2015-02.
I would like to return all ID's and Dates that do not have a value.
Date range is: select distinct date from Table1
I can't seem to think outside the realms of selecting and joining on the same table.
I need to include the ID in my select to I can somehow select the ID and Date range that exists for that ID and compare to the entire date range, to get all the dates for each ID that isn't in the "entire" date range.
Please advise.
Thank you
Not very clear about your last two sentences. But you can play with the following query with different #max_days and #min_date:
-- DROP TABLE table1;
CREATE TABLE table1(ID int not null, `date` date not null, value varchar(64) not null);
INSERT table1(ID,`date`,value)
VALUES (1,'2015-01-01','v1'),(1,'2015-01-02','v2'),(2,'2015-01-01','v1'),(2,'2015-01-03','v2'),(4,'2015-01-01','v1'),(4,'2015-01-04','v2');
SELECT * FROM table1;
SET #day=0;
SET #max_days=5;
SET #min_date='2015-01-01';
SELECT i.ID,d.`date`
FROM (SELECT DISTINCT ID FROM table1) i
CROSS JOIN (
SELECT TIMESTAMPADD(DAY,#day,#min_date) AS `date`,#day:=#day+1 AS day_num
FROM table1 WHERE #day<#max_days) d
LEFT JOIN table1 t
ON t.ID=i.ID
AND t.`date`=d.`date`
WHERE t.`date` IS NULL
ORDER BY i.ID,d.`date`;
I now understand your requirement of dates being taken from the table; you want to find any gaps in the date ranges for each id.
This does what you need, but can probably be improved. Explanation below and you can view a working example.
DROP TABLE IF EXISTS Table1;
DROP TABLE IF EXISTS Year_Month_Calendar;
CREATE TABLE Table1 (
id INTEGER
,date CHAR(7)
,value CHAR(4)
);
INSERT INTO Table1
VALUES
(1,'2015-01','val1')
,(1,'2015-02','val2')
,(2,'2015-01','val1')
,(2,'2015-03','val1');
CREATE TABLE Year_Month_Calendar (
date CHAR(10)
);
INSERT INTO Year_Month_Calendar
VALUES
('2015-01')
,('2015-02')
,('2015-03');
SELECT ID_Year_Month.id, ID_Year_Month.date, Table1.id, Table1.date
FROM (
SELECT Distinct_ID.id, Year_Month_Calendar.date
FROM Year_Month_Calendar
CROSS JOIN
( SELECT DISTINCT id FROM Table1 ) AS Distinct_ID
WHERE Year_Month_Calendar.date >= (SELECT MIN(date) FROM Table1 WHERE id=Distinct_ID.ID)
AND Year_Month_Calendar.date <= (SELECT MAX(date) FROM Table1 WHERE id=Distinct_ID.ID)
) AS ID_Year_Month
LEFT JOIN Table1
ON ID_Year_Month.id = Table1.id AND ID_Year_Month.date = Table1.date
-- WHERE Table1.id IS NULL
ORDER BY ID_Year_Month.id, ID_Year_Month.date
Explanation
You need a calendar table which contains all dates (year/months) to cover the data you are querying.
CREATE TABLE Year_Month_Calendar (
date CHAR(10)
);
INSERT INTO Year_Month_Calendar
VALUES
('2015-01')
,('2015-02')
,('2015-03');
The inner select creates a table with all dates between the min and max date for each id.
SELECT Distinct_ID.id, Year_Month_Calendar.date
FROM Year_Month_Calendar
CROSS JOIN
( SELECT DISTINCT id FROM Table1 ) AS Distinct_ID
WHERE Year_Month_Calendar.date >= (SELECT MIN(date) FROM Table1 WHERE id=Distinct_ID.ID)
AND Year_Month_Calendar.date <= (SELECT MAX(date) FROM Table1 WHERE id=Distinct_ID.ID)
This is then LEFT JOINED to the original table to find the missing rows.
If you only want to return the missing row (my query displays the whole table to show how it works), add a WHERE clause to restrict the output to those rows where an id and date is not returned from Table1
Original answer before comments
You can do this without a tally table, since you say
Date range is: select distinct date from Table1
I've slightly changed the field names to avoid reserved words in SQL.
SELECT id_table.ID, date_table.`year_month`, table1.val
FROM (SELECT DISTINCT ID FROM table1) AS id_table
CROSS JOIN
(SELECT DISTINCT `year_month` FROM table1) AS date_table
LEFT JOIN table1
ON table1.ID=id_table.ID AND table1.`year_month` = date_table.`year_month`
ORDER BY id_table.ID
I've not filtered the results, in order to show how the query is working. To return the rows where only where a date is missing, add WHERE table1.year_month IS NULL to the outer query.
SQL Fiddle
You will need a tally table(s) or month/year tables. So you can then generate all of the potential combinations you want to test with. As far as exactly how to use it your example could use some expanding on such as last 12 months, last3 months, etc. but here is an example that might help you understand what you are looking for:
http://rextester.com/ZDQS5259
CREATE TABLE IF NOT EXISTS Tbl (
ID INTEGER
,Date VARCHAR(10)
,Value VARCHAR(10)
);
INSERT INTO Tbl VALUES
(1,'2015-01','val1')
,(1,'2015-02','val2')
,(2,'2015-01','val1')
,(2,'2015-03','val1');
SELECT yr.YearNumber, mn.MonthNumber, i.Id
FROM
(
SELECT 2016 as YearNumber
UNION SELECT 2015
) yr
CROSS JOIN (
SELECT 1 MonthNumber
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8
UNION SELECT 9
UNION SELECT 10
UNION SELECT 11
UNION SELECT 12
) mn
CROSS JOIN (
SELECT DISTINCT ID
FROM
Tbl
) i
LEFT JOIN Tbl t
ON yr.YearNumber = CAST(LEFT(t.Date,4) as UNSIGNED)
AND mn.MonthNumber = CAST(RIGHT(t.Date,2) AS UNSIGNED)
AND i.ID = t.ID
WHERE
t.ID IS NULL
The basic idea to determine what you don't know is to generate all possible combinations of something could be. E.g. Year X Month X DISTINCT Id and then join back to figure out what is missing.
Probably not the prettiest but this should work.
select distinct c.ID, c.Date, d.Value
from (select a.ID, b.Date
from (select distinct ID from Table1) as a, (select distinct Date from Table1) as b) as c
left outer join Table1 d on (c.ID = d.ID and c.Date = d.Date)
where d.Value is NULL

How can I run a second query against results from my first query?

Here is the updated scenario, hope this is clearer than the last version.
Table 1 is where all data for this process is housed and contains around 5 million records.
`table1`
ID Forename Surname Tel Source Optin DistributedDate
1 A Test 0131 TL037 NULL NULL
2 B Test 0141 TL035 NULL NULL
v v v v v v v
Table 2 is updated via an insert script and contains a distinct list of sources used within a period of time, this could just as easily be created by a view.
INSERT INTO table2(`Sourcecode`)
(SELECT DISTINCT(a.`Sourcecode`) FROM `table1` a)
`table2 - structure`
ID Sourcecode
1 TL037
2 TL031
3 TL004
4 TL029
5 TL035
I am now trying to pull back the details of 5 random records from each distinct source from table 2 and insert these details into table 3.
`table3 - structure`
ID Forename Surname Tel Source Optin DistributedDate
NULL NULL NULL NULL NULL NULL NULL
This is the code I've created so far:
SET #Sourcecode =
(SELECT b.`Sourcecode`
FROM `table2` b
WHERE b.id = b.id
AND b.`Sourcecode` NOT IN (SELECT DISTINCT(source) FROM `table3`)
LIMIT 1);
INSERT INTO table3.*
(SELECT
a.id AS 'ID',
a.`FirstName` AS 'Forename',
a.`Surname` AS 'Surname',
a.`TelephoneNumber2` AS 'Tel',
a.`SourceCode` AS 'Source',
a.`optin` AS 'Optin',
a.`DateExported` AS 'DistributedDate'
FROM `table1`
WHERE a.`SourceCode` = #SourceCode
ORDER BY RAND(a.sourcecode)
LIMIT 5
Basically the end result I'm looking for is to run the above queries multiple times automatically from the result-set against #Sourcecode until value is NULL.
I've tried this using the loop function but I'm not having much luck with it.
You can create a View like this:
CREATE VIEW query1 AS
SELECT DISTINCT(b.`source`)
FROM `table2` b
Then collect from the View as:
SELECT *
FROM `table1` a
WHERE a.`source` IN(DISTINCT(b.`source`) FROM `query1` b)
ORDER BY RAND(a.id)
LIMIT 5)
I haven't tested it myself, if this doesn't work, please give some source and expected data.
Try this:
SELECT a.id, A2.RAND_NUMBER
FROM `table1` a, (SELECT DISTINCT(b.`source`)
,ABS(CHECKSUM(NewId())) % #INT AS RAND_NUMBER
FROM `table2` b) AS A2
WHERE A2.RAND_NUMBER BETWEEN 1 AND 5
GROUP BY a.id, A2.RAND_NUMBER
ORDER BY RAND(a.id)

Retrieve a value from either mysql table

I have table a and b, each have an average field in them. Data is inserted into only one of the tables at a time so either of the average fields can remain null at one given time. How can i retrieve the value in either of the fields in the two tables which is not null
Table a
id average labref
1 325 123
Table b
id average labref
2 null 123
If table a is the one with the average value, i pick that value and if next time table b is the a with the average value and table one is average is null, i pick the value of table a. They both have same id used called labref!
select average from (
select average from tablea
union
select average from tableb) a
where average is not null
OR
select CASE WHEN a.average is null then b.average else a.average end average from tablea a inner join table b
on a.labref=b.labref
SELECT
IF(a.average IS NULL, b.average, a.average) AS average
FROM
a, b
where a.id = b.id
Try:
select labref, max(average) from
(select labref, average from a union all
select labref, average from b) ab
group by labref
You can try this
select average from a, b where a.average IS NOT NULL and b.average IS NOT NULL
i am not sure of to use IS NOT NULL two times or not.
Could you use the IFNOTNULL() function in the following way :
select IFNOTNULL(average,(select average from b)) from a
I havent tested this and am going off the top of my head.