Splitting a value from postcode within sql to group and results - php

I am having some issues with this part of my query, hopefully someone can advise me, Currently i have the postcodes grouping as a complete postalcode but i would like to split this value, and group by the first part of postcode
Perhaps I am overlooking the simple solution to this?
Here is my current query
select *, count(jobline_pickup_add_postalcode) as totals
FROM rhrj_jobline
WHERE jobline_date >= '$start' AND jobline_date <= '$end'
GROUP BY jobline_pickup_add_postalcode
Any advice would be awesome
EDIT:
My postcodes look like this
KA12 0RA - 1
KA15 1JG - 26
KA15 2AT - 1
KA15 2LF - 1
KA151JG/PA2 6LA - 2

select SUBSTRING_INDEX(jobline_pickup_add_postalcode, ' ', 1) AS papc, count(jobline_pickup_add_postalcode) as totals FROM rhrj_jobline WHERE jobline_date >= '$start' AND jobline_date <= '$end' GROUP BY papc

Related

PHP comparison operator not working in MySQL streak query

In attempting to calculate the longest goalscoring streak for a single player, I've hit a stumbling block with the >= PHP operator not performing as intended.
The data is presented in a table as follows:
date gls
------------------------
1980-08-16 2
1980-08-19 1
1980-08-23 1
1980-08-26 0
1980-08-30 1
1980-09-06 2
1980-09-13 0
... and so on
The PHP query I am using is as follows:
SELECT gls, MIN(date) as StartDate, MAX(date) as EndDate, COUNT(*) as Games
FROM (SELECT gls, date,
(SELECT COUNT(*)
FROM goalsengine G
WHERE G.gls <> GE.gls
AND G.date <= GE.date) as RunGroup
FROM goalsengine GE) A
WHERE gls>='1'
GROUP BY gls, RunGroup
ORDER BY Games
I formed the query this way in the belief that >= would tally up streaks where this player had scored one or more goals in a game. From the table above, the first three entries would represent a scoring run of three games, for example.
Instead, the query is returning streaks but only where a certain number of goals are scored i.e. despite scoring in the first three games, the first entry (where two goals were scored, not one) is ignored when the streak is returned.
To return the results, I am using the following:
while($row=mysql_fetch_assoc($result))
{
{$startrundate = date("d F Y",strtotime($row['StartDate']));}
{$endrundate = date("d F Y",strtotime($row['EndDate']));}
echo "<tr>";
echo "<td>".$row['Games']."</td>";
echo "<td class='tableprofile' style='text-align:right;'>".$startrundate." - ".$endrundate."</td>";
echo "</tr>";
$rowCount += 1;
}
By comparison, WHERE gls='0' is giving me the desired non-goalscoring streaks. I feel I have perhaps overlooked something straightforward but cannot see what.
I've managed to work out a solution to this particular issue and it works as far as I can tell, having counted some of the raw data out manually.
SELECT gls, MIN(date) as StartDate, MAX(date) as EndDate, COUNT(*) as Games
FROM (SELECT gls, date,
(SELECT COUNT(*)
FROM goalsengine G
WHERE IF(G.gls IN ('1','2','3','4','5'),1,0) <> IF(GE.gls IN ('1','2','3','4','5'),1,0)
AND G.date <= GE.date) as RunGroup
FROM goalsengine GE) A
WHERE gls IN ('1','2','3','4','5') GROUP BY IF(bg IN ('1','2','3','4','5'),1,0), RunGroup ORDER BY Games DESC LIMIT 3");
I admit it's not exactly pretty, but it's doing the job for now. I have gone to 5 in the calculation as that's the maximum number of goals scored in a single match with regards to this data set.

Finding available rooms from datespan

I have a start date and a end date.
$s="YYYY-MM-DD";
$e="YYYY-MM-DD";
I need to make a query that checks the available rooms for that span.
cObjects , cBookings_details
cObjects_id = cBookings_details_cObjects_id
Im really stuck here, would really appreciate some help.
My Current Query
SELECT cObjects.*
FROM cObjects LEFT JOIN cBookings_details ON cObjects.cObjects_id = cBookings_details.cBookings_details_cObjects_id
WHERE cBookings_details.cBookings_details_arrival NOT BETWEEN '".$s."' AND '".$e."'
AND cBookings_details.cBookings_details_departure NOT BETWEEN '".$s."' AND '".$e."'
AND cBookings_details.cBookings_details_arrival <> '".$s."' AND cBookings_details_departure <> '".$s."'
AND cBookings_details.cBookings_details_arrival <> '".$e."' AND cBookings_details_departure <> '".$e."'
AND cObjects.cObjects_type < '2'
OR cBookings_details.cBookings_details_id is null AND cObjects_type < '2'
This is a common problem.. actually just wrote a guide for this at work:
Selecting rows with start and end dates that feature in a chosen interval
-------------------------------------------------------------------------
Chosen Interval - :start_date (S)
:end_date (E)
Rows - row.start_date (s)
row.end_date (e)
Increasing date-->
S E
| | Chosen Interval
1. s---e Rows we want
2. s---e .
3. s---e .
4. s----------e .
5. s--e Rows we don't want
6. s--e .
LOGIC:
row.end_date >= :start_date AND row.start_date <= :end_date
If you are looking for available rooms, LEFT JOIN the room to the bookings using the logic above and SELECT rows where the booking is NULL.
Applied to your schema I believe this would come out as:
SELECT o.*
FROM cObjects o
LEFT JOIN cBookings_details bd
ON bd.cBookings_details_cObjects_id = o.cObjects_id
AND bd.cBookings_details_arrival <= :end_date
AND bd.cBookings_details_departure >= :start_date
WHERE bd.cBookings_details_id IS NULL
(AND o.cObjects_type < '2') # Double check this is what you want
By the way, I would cry if I had to work with your schema. Why repeat the table name in the column? It just adds noise.

to accelerate mysql query

I have this query in MySQL. This query is taking too long to run, and I know the problem is the selectors (coalesce ((SELECT ...), I do not know how to speed up a query, via join.
I am hoping some of you SQL gurus will be able to help me.
SELECT
COALESCE(
(SELECT CONCAT(d.PRIJEVOZNIK, ' ', d.VOZAC_TRANSFER)
FROM dokum_zag as d
where d.SIFKNJ='NP' and
d.ID_VEZA=dokum_zag.ID and
d.korisnicko_ime=dokum_zag.korisnicko_ime
),'') as PRIJEVOZNIK,
(RELACIJA_TRANS_VOZ_TRANS) as RELACIJA_TRANS_VOZ,
(PRIJEVOZNIK_POVRATNI_TRANS) as PRIJEVOZNIK_POVRATNI,
(VAUC_KNJIZENO_TRANS) as VAUC_KNJIZENO,
ID_NALOGA,
ID_NALOGA_POV,
ID_VAUCHER,
DOLAZAK, VRIJ_TRANSFER,ODLAZAK,VRIJEME_LETA_POVRAT ,BRDOK, NOSITELJ_REZ, RELACIJA_TRANS, VOZILO_NAZIV, BROJ_NALOGA,BROJ_NAL_POV,BROJ_VAUCHER,BROJ_SOBE,VALIZN,PAX, MPIZN,ID
FROM
dokum_zag
WHERE
korisnicko_ime = '10' and
((DOLAZAK='2015-07-30') or (ODLAZAK='2015-07-30')) and
STORNO <> 'DA' and
SIFKNJ = 'TR' and
((YEAR(DOLAZAK)= '2015') or (YEAR(ODLAZAK)= '2015'))
order by
(CASE WHEN DOLAZAK < '2015-07-30' THEN ODLAZAK ELSE DOLAZAK END) ,
(CASE WHEN DOLAZAK < '2015-07-30' THEN VRIJEME_LETA_POVRAT ELSE VRIJ_TRANSFER END), ID
Without a DB structure, and a description of what you want to extract it's a bit hard to help you.
From a logical point of view, somethings are redundant, for example
((DOLAZAK='2015-07-30') or (ODLAZAK='2015-07-30')) and
...
((YEAR(DOLAZAK)= '2015') or (YEAR(ODLAZAK)= '2015'))
The year part isn't necessary since the year is specified on the first two.
Another thing that might be making the server nuts, is that weird order by clause, since it changes from record to record (test this setting it fixed on a field).
You can also check if your indexes are properly set for all the fields on the external where clause, and those which are not numeric, are not varchars (for example SIFKNJ and STORNO should be char(2) ).
The coalesce part can be solved via an outer join, so it doesn't get calculated on each row. But that depends on what and how you want to extract from the database... (since that subquery has it's own fields on the where section... weird)
Hope this somehow helps
INDEX(korisnicko_ime, SIFKNJ)
(in either order) may help
Turning the correlated subquery into a JOIN may help.
((DOLAZAK='2015-07-30') or (ODLAZAK='2015-07-30')) and
((YEAR(DOLAZAK)= '2015') or (YEAR(ODLAZAK)= '2015'))
is a bit weird. This might help:
( SELECT ...
AND DOLAZAK ='2015-07-30' AND ODLAZAK >= '2015-01-01'
AND ODLAZAK < '2015-01-01' + INTERVAL 1 YEAR
) UNION DISTINCT
( SELECT ...
AND ODLAZAK ='2015-07-30' AND DOLAZAK >= '2015-01-01'
AND DOLAZAK < '2015-01-01' + INTERVAL 1 YEAR
) ORDER BY ...
To help that reformulation, add 2 composite indexes:
INDEX(korisnicko_ime, SIFKNJ, DOLAZAK, ODLAZAK)
INDEX(korisnicko_ime, SIFKNJ, ODLAZAK, DOLAZAK)

Counting payment from date range in MySQL

I want to count payment within selected date, but i can't figure it out how to do it.
Here is the example data from the my table
id starts_from payment_per_day
=======================================
1 2012-01-01 10,000.00
2 2012-01-15 10,500.00
3 2012-02-01 11,000.00
4 2012-02-15 11,500.00
5 2012-03-01 12,000.00
How do i count total payment from 2012-01-21 to 2012-02-20 ?
The total payment should be 338,500
from 2012-01-21 to 2012-01-31 = 11 days * 10,500
from 2012-02-01 to 2012-02-14 = 14 days * 11,000
from 2012-02-15 to 2012-02-20 = 6 days * 11,500
But if i do :
SELECT SUM(payment_per_day) as total FROM table
WHERE starts_from BETWEEN '2012-01-21' AND '2012-02-20'
the result is only 22,500
Any ideas ?
SELECT SUM(payment_per_day) as total FROM table
WHERE starts_from BETWEEN '2012-01-21' AND '2012-02-20';
I would first expand the range into the list of dates, then use the following query:
SELECT SUM(p1.payment_per_day)
FROM dates d
INNER JOIN payments p1 ON p1.starts_from <= d.date
LEFT JOIN payments p2 ON p2.starts_from <= d.date
AND p2.starts_from > p1.starts_from
WHERE p2.id IS NULL
You could obtain the list from the range with the help of a numbers table, like this:
SELECT DATE_ADD(#date_from, INTERVAL num DAY)
FROM numbers
WHERE num BETWEEN 0 AND DATEDIFF(#date_to, #date_from)
A numbers table is a thing worth having as it can be useful in many situations, so consider providing yourself with one. Here's a very simple script to create and initialise a numbers table:
CREATE TABLE numbers AS SELECT 0 AS num;
SET #ofs = (SELECT COUNT(*) FROM numbers); INSERT INTO numbers SELECT #ofs + num FROM numbers;
SET #ofs = (SELECT COUNT(*) FROM numbers); INSERT INTO numbers SELECT #ofs + num FROM numbers;
SET #ofs = (SELECT COUNT(*) FROM numbers); INSERT INTO numbers SELECT #ofs + num FROM numbers;
… /* repeat as necessary, each line doubles the number of rows in the table */
But, of course, you can use a loop instead.
Here's my complete testing environment on SQL Fiddle (for anyone to play with).
It seems that it is almost impossible to do query like that, counting total each day payment within selected date.
So i work around by selecting data from all starts_from dates until <= 2012-02-20 and then picking last starts_from date which is less than 2012-01-21 (that is 2012-01-15) in order to get payment_per_day 10,500.00
Thank you for viewing my question :)

How could I simplify this SQL query in Oracle?

After searching and reading a little bit I came up with the following SQL query for my application:
SELECT
ROUND(AVG(CASE WHEN gender = 'M' THEN rating END), 1) avgAllM,
COUNT(CASE WHEN gender = 'M' THEN rating END) countAllM,
ROUND(AVG(CASE WHEN gender = 'F' THEN rating END), 1) avgAllF,
COUNT(CASE WHEN gender = 'F' THEN rating END) countAllF,
ROUND(AVG(CASE WHEN gender = 'M' AND UserAge(birth_date) <= 18 THEN rating END), 1) avgU18M,
COUNT(CASE WHEN gender = 'M' AND UserAge(birth_date) <= 18 THEN rating END) countU18M,
ROUND(AVG(CASE WHEN gender = 'F' AND UserAge(birth_date) <= 18 THEN rating END), 1) avgU18F,
COUNT(CASE WHEN gender = 'F' AND UserAge(birth_date) <= 18 THEN rating END) countU18F
FROM movie_ratings mr INNER JOIN accounts a
ON mr.aid = a.aid
WHERE mid = 5;
And I'm wondering how can I simplify this, if possible. The birth_date field is of type DATE and UserAge is a function to calculate the age from that date field.
The table structures are as follows:
[ACCOUNTS]
aid(PK), birth_date, gender
[MOVIE_RATINGS]
mid(PK), aid(PK,FK), rating
I'm looking for two things:
General simplifications to the code above that more experienced users know about that I don't.
I'm doing this in PHP and for each record I'll have an associative array with all those variables. I'm looking for a way to group them into a multidimensional array, so the PHP code is easier to read. Of course I don't want to do this in PHP itself, it would be pointless.
For instance, something like this:
$info[0]['avgAllM']
$info[0]['countAllM']
$info[1]['avgAllF']
$info[1]['countAllF']
$info[2]['avgU18M']
$info[2]['countU18M']
$info[3]['avgU18F']
$info[3]['countU18F']
Instead of:
$info['avgAllM']
$info['countAllM']
$info['avgAllF']
$info['countAllF']
$info['avgU18M']
$info['countU18M']
$info['avgU18F']
$info['countU18F']
I don't even know if this is possible, so I'm really wondering if it is and how it can be done.
Why I want all this? Well, the SQL query above is just a fragment of the complete SQL I need to do. I haven't done it yet because before doing all the work, I want to know if there's a more compact SQL query to achieve the same result. Basically I'll add a few more lines like the ones above but with different conditions, specially on the date.
You could create a VIEW with the following definition
SELECT
CASE WHEN gender = 'M' THEN rating END AS AllM,
CASE WHEN gender = 'F' THEN rating END AS AllF,
CASE WHEN gender = 'M' AND UserAge(birth_date) <= 18 THEN rating END AS U18M,
CASE WHEN gender = 'F' AND UserAge(birth_date) <= 18 THEN rating END AS U18F
FROM movie_ratings mr INNER JOIN accounts a
ON mr.aid = a.aid
WHERE mid = 5
Then SELECT from that
SELECT ROUND(AVG(AllM), 1) avgAllM,
COUNT(AllM) countAllM,
ROUND(AVG(AllF), 1) avg,
COUNT(AllF) countAllF,
ROUND(AVG(U18M), 1) avgU18M,
COUNT(U18M) countU18M,
ROUND(AVG(U18F), 1) avgU18F,
COUNT(U18F) countU18F
FROM yourview
Might simplify things slightly?
This could just be a case of optimizing too early. The query does what you need and only really looks complicated because it is. I'm not sure that there are necessarily any tricks that would help. It probably depends on the characteristics of your data. Is the query slow? Do you think it could be quicker?
It might be worth rearranging it in the following way. Since all the conditions rely on the ACCOUNTS table which I assume is going to be significantly smaller than the MOVIE_RATINGS table you might be able to do all the calculations on a smaller data set, which might be quicker. Although if you are only selecting on one movie at a time (mid = 5) then that probably won't be the case.
I'm not entirely sure that this will work but think it should.
SELECT
ROUND(AVG(rating * AllM), 1) avgAllM,
COUNT(rating * AllM) countAllM,
ROUND(AVG(rating * AllF), 1) avgAllF,
COUNT(rating * AllF) countAllF,
ROUND(AVG(rating * AllM * U18), 1) avgU18M,
COUNT(rating * AllM * U18) countU18M,
ROUND(AVG(rating * AllM * U18), 1) avgU18F,
COUNT(rating * AllM * U18) countU18F
FROM
movie_ratings mr
INNER JOIN (
select
aid,
case when gender = 'M' then 1 end as AllM,
case when gender = 'F' then 1 end as AllF,
case when UserAge(birth_date) <= 18 then 1 end as U18
from accounts) a ON mr.aid = a.aid
WHERE mid = 5;
On balance though, I would probably just leave the query you have as it is. The query that you have is easy to understand and probably performs fairly well.

Categories