Using date ranges in SQL - mysql

I access SQL and the database through an ODBC connection via Excel. This way I can create refresh-able reports. I use a ? when using the date between condition so that I can use this report often with different date ranges.
The issue is, when I use OR conditions I end up having to put in the date range 4 times, or entering 8 different dates. Is there someway to simplify the below, so that I don't have to enter in the date as many times?
Select CFF2X AS TYPE, Count(*) AS COUNT
FROM ZBP602F.SIH
JOIN ZBP602UF.NRCMCVL1 ON SICUST=CUSTX
WHERE SIINVD BETWEEN ? AND ? AND SICOMP IN (01, 03, 06) and SITOT <> 0 and CFF2X <> (' ') AND IHOCLS IN (004, 800, 007, 100, 008, 102, 104, 140, 105, 110, 111,109)
OR SIINVD BETWEEN ? AND ? AND SICOMP IN (01, 03, 06) and SITOT <> 0 and CFF2X = (' ') AND IHOCLS IN (004, 800, 007, 008, 100, 102, 104, 140, 105, 110, 111)
OR SIINVD BETWEEN ? AND ? AND SICOMP IN (01, 03, 06) and SITOT <> 0 AND IHOCLS IN (114) and SIPAY in ('A', 'E')
OR SIINVD BETWEEN ? AND ? AND SICOMP IN (01, 03, 06) and SITOT <> 0 AND IHOCLS IN (114) and SIPAY = 'B' and CFF2X <> (' ')
Group by CFF2X

WHERE SIINVD BETWEEN ? AND ? AND SICOMP IN (01, 03, 06) AND SITOT <> 0
AND (
(CFF2X <> ' ' AND
IHOCLS IN (004, 800, 007, 100, 008, 102, 104, 140, 105, 110, 111,109)
)
OR (CFF2X = ' ' AND
IHOCLS IN (004, 800, 007, 008, 100, 102, 104, 140, 105, 110, 111)
)
OR (IHOCLS = 114 and SIPAY in ('A', 'E'))
OR (IHOCLS = 114 and SIPAY = 'B' and CFF2X <> ' ')
)
You're simply putting the unchanging items in once and then putting the changing bits in as a couple of OR statements. The process of simplifying boolean logic like this is called Logic Reduction (think Karnaugh maps). There are additional optimization you can perform here as well.

I'd try using some parentheses to prevent the repetitiveness.
If you change the WHERE to something like:
WHERE (SIINVD BETWEEN ? AND ? AND SICOMP IN (01, 03, 06) and SITOT <> 0) AND
(
(CFF2X <> (' ') AND IHOCLS IN (004, 800, 007, 100, 008, 102, 104, 140, 105, 110, 111,109)) OR
(CFF2X = (' ') AND IHOCLS IN (004, 800, 007, 008, 100, 102, 104, 140, 105, 110, 111)) OR
(IHOCLS IN (114) and SIPAY in ('A', 'E')) OR
(IHOCLS IN (114) and SIPAY = 'B' and CFF2X <> (' '))
)
;
This will check the SIINVD, SICOMP, and SITOT for all records first being that it is the same query for all. Then the 4 different cases will be matched. The current query as you have it may not actually do what you think because of the lack of parenthesis.

Related

MySQL query SELECT inside IN

I'm new in SQL, have a comlpex query for me which choose only one cheapest line from each cityId to each cityId:
SELECT cityIdFrom, cityIdTo, MIN(fromToPrice) fromToPrice_min
FROM pricing
WHERE cityIdFrom IN (91, 94, 95, 99)
AND cityIdTo IN (91, 94, 95, 99)
GROUP BY cityIdFrom, cityIdTo
So we take cheapest from 91 to 94, cheapest from 91 to 95 etc. How can I fix this query to SELECT lines for cityIdFrom 91 only when column Bull IS 1.
For example, if we have:
cityIdFrom - cityIdTo - fromToPrice - Bull
91, 94, 3000, 0
91, 94, 5000, 1
91, 95, 1000, 0
91, 99, 1500, 1
99, 95, 2000, 0
Our query will give us:
91, 94, 5000, 1
91, 99, 1500, 1
99, 95, 2000, 0
Thank for help!
I was all about one line in my Query:
AND (CityIdFrom = 91 AND Bull = 1) OR CityIdFrom <> 91
Many thanks to #ADyson, he helped me so much!

MySQL - group_concat pulling in additional incorrect data

I'm having trouble with a JOIN and a GROUP_CONCAT. The query is concatenating additional data that should not be associated with the join.
Here's my table structure:
linkages
ID table_name tag_id
1 subcategories 6
2 categories 9
music
ID artwork
1 5
2 4
artwork
ID url_path
1 /some/file/path
2 /some/file/path
And here's my query:
SELECT music.*,
artwork.url_path AS artwork_url_path,
GROUP_CONCAT( linkages.tag_id ) AS tag_ids,
GROUP_CONCAT( linkages.table_name ) AS table_name
FROM music
LEFT JOIN artwork ON artwork.id = music.artwork
LEFT JOIN linkages ON music.id = linkages.track_id
WHERE music.id IN( '1356',
'1357',
'719',
'169',
'170',
'171',
'805' )
ORDER BY FIELD( music.id,
1356,
1357,
719,
169,
170,
171,
805 )
This is the result of the GROUP_CONCAT :
[tag_ids] => 3, 6, 9, 17, 19, 20, 26, 49, 63, 64, 53, 57, 63, 65, 67, 73, 79, 80, 85, 96, 98, 11, 53, 67, 3, 6, 15, 17, 26, 38, 50, 63, 74, 53, 56, 57, 62, 63, 65, 66, 67, 72, 85, 88, 98, 24, 69, 71, 3, 6, 15, 17, 26, 38, 50
The first portion of the result is correct:
[tag_ids] => 3, 6, 9, 17, 19, 20, 26, 49, 63, 64, 53, 57, 63, 65, 67, 73, 79, 80, 85, 96, 98, 11, 53, 67
Everything after the correct values seems random and most of the values don't exist in the result in the database, but it's still pulling it in. It seems to repeat a portion of the correct result (3, 6, 15, 17 - the 3, 6, 17 are correct, but 15 shouldn't be there, similar with a bunch of other numbers - 71, etc. I can't use DISTINCT because I need to match up the tag_ids and table_name results as a multidimensional array from the results.
Any thoughts as to why?
UPDATE:
I ended up solving it with the initial push from Gordon. It needed a GROUP_BY clause, otherwise it was putting every results tag id's in each result. The final query ended up becoming this:
SET SESSION group_concat_max_len = 1000000;
SELECT
music.*,
artwork.url_path as artwork_url_path,
GROUP_CONCAT(linkages.tag_id, ':', linkages.table_name) as tags
FROM music
LEFT JOIN artwork ON artwork.id = music.artwork
LEFT JOIN linkages ON music.id = linkages.track_id
WHERE music.id IN('1356', '1357', '719', '169', '170', '171', '805')
GROUP BY music.id
ORDER BY FIELD(music.id,1356,1357,719,169,170,171,805);
Your join is generating duplicate rows. I would suggest that you fix the root cause of the problem. But, a quick-and-dirty solution is to use group_concat(distinct):
GROUP_CONCAT(DISTINCT linkages.tag_id) as tag_ids,
GROUP_CONCAT(DISTINCT linkages.table_name) as table_name
You can put the columns in a single field using GROUP_CONCAT():
GROUP_CONCAT(DISTINCT linkages.tag_id, ':', linkages.table_name) as tags

SQL NOT IN still includes rows that should be excluded

I have the following statement to find rows that include certain values but exclude others:
SELECT *
FROM tests
WHERE author = 4
OR id = -999
OR id = 276
OR id = 343
OR id = 197
OR id = 170
OR id = 1058
OR id = 1328
OR id = 1417
AND is_deleted = 0
AND id NOT IN (457, 2409, 173, 400, 167, 277, 163, 404, 2222, 24, 26,
2457, 16, 25, 1639, 2224, 1804, 2308, 197, 461, 1442,
1594, 460, 1235, 1814, 2467, 168, 172, 170, 171, 2223, 2535, 2754)
However, I am still getting rows that should be exclude, as per the NOT IN list. For example, a test with the id, 16, should be excluded even though the tests.author = 4. But it is being returned in the query, which I don't want.
The statement is created programmatically depending on the situation.
Is there a syntax mistake that I'm making?
Have a look at SQL Server's operator precedence. You'll see that and has a higher precedence than or.
Say that you're looking for a fast car that is red or blue. If you write:
where speed = 'fast' and color = 'green' or color = 'blue'
SQL Server will read:
where (speed = 'fast' and color = 'green') or color = 'blue'
And in response to your query, SQL Server could return a slow blue car.
Change your query to this:
SELECT *
FROM tests
WHERE (author = 4 OR id = -999 OR id = 276 OR id = 343 OR id = 197 OR id = 170 OR id = 1058 OR id = 1328 OR id = 1417)
AND is_deleted = 0
AND id NOT IN (457, 2409, 173, 400, 167, 277, 163, 404, 2222, 24, 26, 2457, 16, 25, 1639, 2224, 1804, 2308, 197, 461, 1442, 1594, 460, 1235, 1814, 2467, 168, 172, 170, 171, 2223, 2535, 2754)
you have to put all your or in parenthesis.
Try this::
SELECT
*
FROM tests
WHERE
(author = 4
OR
id in (-999,276 ,343 ,197 ,170 ,1058 ,1328 ,1417)
AND is_deleted = 0 )
AND id NOT IN (457, 2409, 173, 400, 167, 277, 163, 404, 2222, 24, 26, 2457, 16, 25, 1639, 2224, 1804, 2308, 197, 461, 1442, 1594, 460, 1235, 1814, 2467, 168, 172, 170, 171, 2223, 2535, 2754)
Omair you are misplacing the '(' first of all just be clear that you want to select which author.
Suppose we need,
Authors having author = 4 or whose id is contained in -999, 343, 197 etc and whose deleted status = 0 and ID must not be in 457, 2409 ,...... etc.
What you did was,
author = 4 OR id = -999 OR id = 276 ...
AND is_deleted = 0
AND id NOT IN (457, 2409, 173, 400, 167, 277, 163, 404, 2222, 24, 26, ...)
This is interpreted according to operator precedence as
(author = 4 ) OR ( id = -999 OR id = 276 ...
AND is_deleted = 0
AND id NOT IN (457, 2409, 173, 400, 167, 277, 163, 404, 2222, 24, 26, ...)
)
Here, we just need to add proper '(' to separate our conditions as we need
((author = 4 ) OR ( id = -999 OR id = 276 ...)
AND (is_deleted = 0)
AND (id NOT IN (457, 2409, 173, 400, 167, 277, 163, 404, 2222, 24, 26, ...) )
)
So You can change SQL with proper brackets,
SELECT
*
FROM tests
WHERE
( (author = 4) OR id in (-999,276 ,343 ,197 ,170 ,1058 ,1328 ,1417) )
AND ( is_deleted = 0 )
AND ( id NOT IN (457, 2409, 173, 400, 167, 277, 163, 404, 2222, 24, 26, 2457, 16, 25, 1639, 2224, 1804, 2308, 197, 461, 1442, 1594, 460, 1235, 1814, 2467, 168, 172, 170, 171, 2223, 2535, 2754) )

How to use SUM IF() in MySQL without hard coding values in the query

I have a query that uses SUM IF() to do a cross-tab result set. In this query I have the value sin the SUM IF() hard coded. The problem is that new values are added to the database. Is there a way to write the query without hard coding the values in the SUM IF()? Here is the query:
select storeid, sum(if(marketsegmentid = 6, 1, 0)) as 6,
sum(if(marketsegmentid = 7, 1, 0))
as 7, sum(if(marketsegmentid = 12, 1, 0)) as 12, sum(if(marketsegmentid = 17, 1, 0)) as 17,
sum(if(marketsegmentid = 22, 1, 0)) as 22, sum(if(marketsegmentid = 27, 1, 0)) as 27,
sum(if(marketsegmentid = 32, 1, 0)) as 32, sum(if(marketsegmentid = 37, 1, 0)) as 37,
sum(if(marketsegmentid = 42, 1, 0)) as 42, sum(if(marketsegmentid = 47, 1, 0)) as 47,
sum(if(marketsegmentid = 52, 1, 0)) as 52, sum(if(marketsegmentid = 97, 1, 0)) as 97,
sum(if(marketsegmentid = 102, 1, 0)) as 102, sum(if(marketsegmentid = 107, 1, 0)) as 107,
sum(if(marketsegmentid = 112, 1, 0)) as 112, sum(if(marketsegmentid = 117, 1, 0)) as 117,
sum(if(marketsegmentid = 122, 1, 0)) as 122, sum(if(marketsegmentid = 127, 1, 0)) as 127,
sum(if(marketsegmentid = 132, 1, 0)) as 132, sum(if(marketsegmentid = 137, 1, 0)) as 137,
sum(if(marketsegmentid = 142, 1, 0)) as 142
from storemarketsegments
group by storeid;
The query is used in a report and the results are exported to CSV. The 1's are used as flags in the result set.
The table I am querying is set up like this:
CREATE TABLE storemarketsegments(id INT NOT NULL, marketsegmentid INT NOT NULL);
The marketsegments are kept in a separate table:
CREATE TABLE marketsegment(ID INT NOT NULL AUTO_INCREMENT, PRIMARY
KEY(id), name VARCHAR(45), description VARCHAR(45));
Any help would be greatly appreciated. I am not sure if there is a way to write the query without hard coding the values and I don't mind updating the query in the report whenever new marketsegments are added but thought I would check. Thank you in advance for your assistance.
You can return each count as a separate row, and then filter as needed in the application layer:
select storeid, marketsegmentid, count(*) as Count
from storemarketsegments
group by storeid, marketsegmentid;

Add up a points column based on an id from within the same mysql table

OK the database is layed out as (only columns being used are listed):
Table Name: race_stats
Columns: race_id, user_id, points, tournament_id
Table Name: user
Columns: user_id, driver
Table Name: race
Columns: race_id, race_name
Table Name: tournament
Columns: tournament_id, tournament_name
This is my current query:
$query = "
SELECT user.user_id, user.driver, race_stats.points, race_stats.user_id,
SUM(race_stats.points) AS total_points "."
FROM user, race_stats, tournament, race "."
WHERE race.race_id=race_stats.race_id
AND user.user_id=race_stats.user_id
AND tournament.tournament_id=race_stats.tournament_id
GROUP BY driver
ORDER BY total_points DESC
LIMIT 0, 15
";
Ok the query works but it is adding them all up for all the available races from the race_stats.race_id column as the total points. I have racked my brain beyond recognition to fix this but I just can't quite seem to find the solution I need. I'm sure it has to be an easy fix but I just can't get it. Any help is greatly appreciated.
///////////////////EDITED WITH RAW VALUES//////////////////////
INSERT INTO `race_stats` (`id_race`, `race_id`, `user_id`, `f`, `s`, `race_interval`, `race_laps`, `led`, `points`, `total_points`, `race_status`, `tournament_id`, `driver`, `tournament_name`) VALUES
(1, 1, 4, 1, 4, '135.878', 60, '2', 180, 0, 'Running', 1, 'new_driver_5', ''),
(2, 1, 2, 2, 2, '-0.08', 60, '22', 175, 0, 'Running', 1, 'new_driver_38', ''),
(3, 1, 5, 3, 5, '-11.82', 60, '2', 170, 0, 'Running', 1, 'new_driver_94', ''),
(4, 2, 2, 1, 15, '138.691', 29, '6', 180, 0, 'Running', 2, 'new_driver_38', ''),
(5, 2, 15, 2, 9, '-16.12', 29, '8*', 180, 0, 'Running', 2, 'new_driver_44', ''),
(6, 2, 8, 3, 11, '-2:03.48', 29, '0', 165, 0, 'Running', 2, 'new_driver_83', ''),
Let me know if this is what you meant by raw values if not I can get some more data for you.
Just posting the solution here for completeness:
SELECT user.driver, race_stats.race_id,
SUM(race_stats.points) AS total_points "."
FROM user, race_stats "."
WHERE user.user_id=race_stats.user_id
GROUP BY user.driver, race.race_id
Here's the query you want (formatted for readability):
SELECT
u.driver,
SUM(rs.points) AS total_points
FROM user u
LEFT JOIN race_stats rs on rs.user_id = u.user_id
GROUP BY 1;
The advantage of using an outer join (ie LEFT JOIN) is that drivers who have no stats still get a row, but with null as total_points.
p.s. I don't know what the usage of "." in your query is all about, so I removed it.