PostgreSQL: strange collision of ORDER BY and LIMIT/OFFSET - sql

I'm trying to do this in PostgreSQL 9.1:
SELECT m.id, vm.id, vm.value
FROM m
LEFT JOIN vm ON vm.m_id = m.id and vm.variation_id = 1
ORDER BY lower(trim(vm.value)) COLLATE "C" ASC LIMIT 10 OFFSET 120
The result is:
id | id | value
----+-----+---------------
504 | 511 | "andr-223322"
506 | 513 | "andr-322223"
824 | 831 | "angHybrid"
866 | 873 | "Another thing"
493 | 500 | "App update required!"
837 | 844 | "App update required!"
471 | 478 | "April"
905 | 912 | "Are you sure you want to delete this thing?"
25 | 29 | "Assignment"
196 | 201 | "AT ADDRESS"
Ok, let's execute the same query with OFFSET 130:
id | id | value
----+-----+---------------
196 | 201 | "AT ADDRESS"
256 | 261 | "Att Angle"
190 | 195 | "Att Angle"
273 | 278 | "Att Angle:"
830 | 837 | "attAngle"
475 | 482 | "August"
710 | 717 | "Averages"
411 | 416 | "AVG"
692 | 699 | "AVG SHAPE"
410 | 415 | "AVGs"
and we see our AT ADDRESS item again, but at the beginning!!!
The fact is that the vm table contains two following items:
id | m_id | value
----+------+---------------
201 | 196 | "AT ADDRESS"
599 | 592 | "At Address"
I cure this situation with a workaround:
(lower(trim(vm.value)) || vm.id)
but What The Hell ???!!!
Why do I have to use a workaround?

Swearing won't change the SQL standard that defines this behavior.
The order of rows is undefined unless specified in ORDER BY. The manual:
If sorting is not chosen, the rows will be returned in an unspecified
order. The actual order in that case will depend on the scan and join
plan types and the order on disk, but it must not be relied on. A
particular output ordering can only be guaranteed if the sort step is explicitly chosen.
Since you didn't define an order for these two peers (in your sort order):
id | m_id | value
----+------+---------------
201 | 196 | "AT ADDRESS"
599 | 592 | "At Address"
.. you get arbitrary ordering - whatever is convenient for Postgres. A query with LIMIT often uses a different query plan, which can explain different results.
Fix
ORDER BY lower(trim(vm.value)) COLLATE "C", vm.id;
Or (maybe more meaningful - possibly also tune to existing indexes):
ORDER BY lower(trim(vm.value)) COLLATE "C", vm.value, vm.id;
(This is unrelated to the use of COLLATE "C" here, btw.)
Don't concatenate for this purpose, that's much more expensive and potentially makes it impossible to use an index (unless you have an index on that precise expression). Add another expression that kicks in when prior expressions in the ORDER BY list leave ambiguity.
Also, since you have a LEFT JOIN there, rows in m without match in vm have null values for all current ORDER BY expressions. They come last and are sorted arbitrarily otherwise. If you want a stable sort order overall, you need to deal with that, too. Like:
ORDER BY lower(trim(vm.value)) COLLATE "C", vm.id, m.id;
Asides
Why store the double quotes? Seems to be costly noise. You might be better off without them. You can always add the quotes on output if need be.
Many clients cannot deal with the same column name multiple times in one result. You need a column alias for at least one of your id columns: SELECT m.id AS m_id, vm.id AS vm_id .... Goes to show why "id" for a column is an anti-pattern to begin with.

Related

select from table with between

please, help advice.
I have a table.
id|score_max|score_min| segment
--|---------|---------|--------
1 |264 | |girl
2 |263 | 250 |girl+
3 |249 | 240 |girl
4 | | 239 |girl
It is not necessary to obtain a value depending on the value of the score.
But it can be null.
For example, 260 is value from other table
select segment
from mytable
where score_max<260 and score_min>260
Output:
2 |263 | 250 |girl+
but if value =200, sql is not correct
How to make a request correctly?
For this sample data that makes more sense:
id|score_max|score_min| segment
--|---------|---------|--------
1 | | 264 |girl
2 |263 | 250 |girl+
3 |249 | 240 |girl
4 |239 | |girl
you can get the result that you want like this:
select *
from tablename
where
(? >= score_min or score_min is null)
and
(? <= score_max or score_max is null)
Replace ? with the value that you search for.
See the demo.

Oracle, Mysql, how to get average

How to get Average fuel consumption only using MySQL or Oracle:
SELECT te.fuelName,
zkd.fuelCapacity,
zkd.odometer
FROM ZakupKartyDrogowej zkd
JOIN TypElementu te
ON te.typElementu_Id = zkd.typElementu_Id
AND te.idFirmy = zkd.idFirmy
AND te.typElementu_Id IN (3,4,5)
WHERE zkd.idFirmy = 1054
AND zkd.kartaDrogowa_Id = 42
AND zkd.data BETWEEN to_date('2015-09-01','YYYY-MM-DD')
AND to_date('2015-09-30','YYYY-MM-DD');
Result of this query is:
fuelName | fuelCapacity | odometer | tanking
-------------------------------------------------
'ON' | 534 | 1284172 | 2015-09-29
'ON' | 571 | 1276284 | 2015-09-02
'ON' | 470 | 1277715 | 2015-09-07
'ON' | 580.01 | 1279700 | 2015-09-11
'ON' | 490 | 1281103 | 2015-09-17
'ON' | 520 | 1282690 | 2015-09-23
We can do it later in java or php, but want to get result right away from query. How should we modify above query to do that?
fuelCapacity is the number of liters of fuel that has been poured into cartank at gas station.
For one total average, what you need is the sum of the refills divided by the difference between the odometer readings at the start and the end, i.e. fuel used / distance travelled.
I don't have your table structure at hand, but this alteration to the select statement should do the trick:
select cast(sum(zkd.fuelCapacity) as float) / (max(zkd.odometer) - min(zkd.odometer)) as consumption ...
The cast(field AS float) does what the name implies, and typecasts the field as float, so the result will also be a float. (I do suspect that your fuelCapacity field is a float because there is one float value in your example, but this will make sure.)

How to determine value changes according to another columns in Oracle

Below table includes non-unique id, money value and dates/times.
id_1 value_1 value_time id_version Version_time
138 250 09-SEP-14 595 02-SEP-14
140 250 15-SEP-14 695 01-AUG-14
140 300 30-DEC-14 720 05-NOV-14
233 250 01-JUN-15 800 16-MAY-15
As you can see id_1, id_version and time columns can change in table but value_1 may stay the same.
I know that if id_1 is same in rows, value_1 can only change according to id_version. But there are too many id_version in the table. And I know that it changes according to id_version, but i don't know the exact change time of it.
So firstly I have to decide, which id_version and id_version time cause the value change group by id_1.
But again id_1 is not uniqe, and id may change but value stays the same :)
editor: From OP's comment - Begin
Here is the desired result example i want to get the first and second row not the third and fourth row.
| 140 | 250 | 15-SEP-14 | 695 | 01-AUG-14 |
| 140 | 300 | 31-DEC-14 | 725 | 07-NOV-14 |
| 140 | 300 | 05-JAN-14 | 740 | 30-NOV-14 |
| 140 | 300 | 30-DEC-14 | 720 | 05-NOV-14 |
editor: From OP's comment - End
Thanks in advance really need help in this situation.
Based on the input given so far (and processing just the data in the linked to picture - rather than the one in the current example data), the following should help to get you started:
SELECT
TMin.id_1
, TMin.value_1
, TO_CHAR(TAll.value_time, 'DD-MON-RR') value_time
, TMin.id_version
, TO_CHAR(TMin.version_time, 'DD-MON-RR') version_time
FROM
(SELECT
id_1
, value_1
, MIN(id_version) id_version
, MIN(version_time) version_time
FROM T
GROUP BY id_1, value_1
ORDER BY id_1, value_1
) TMin
JOIN T TAll
ON TMin.id_1 = TAll.id_1
AND TMin.value_1 = TAll.value_1
AND TMin.id_version = TAll.id_version
AND TMin.version_time = TAll.version_time
ORDER BY TMin.id_1, TMin.value_1
;
See it in action: SQL Fiddle.
Please comment, if and as this requires adjustment / further detail.

How to combine 4 columns from 2 Access queries into 3 columns of single query?

This question is similar to the one here stackoverflow however it does differ.
I am trying to combine two different queries into one query. Each query has 2 columns. Here is some sample data.
Query #1
Country | Quantity
USA | 312
Canada | 513
Mexico | 258
Query #2
Country | Quanity
USA | 425
UK | 394
Mexico | 489
And then the final query needs to look like this...
Country | Query#1 | Query #2
USA | 312 | 425
Canada | 513 | 0
UK | 0 | 394
Mexico | 258 | 489
The empty ones can be blank, or 0 it does not matter.
It seems like I need to use a FULL OUTER JOIN, or something to that affect, but Access does not have a FULL OUTER JOIN.
I have tried to use a union query, but I do not understand them well enough to accomplish this. Any help is much appreciated!
You want to create a query that joins your other two queries. In order to get nulls (or zeros) from both Queries, you'll need to have a table and/or a Query that has all the countries. Then, you can outer join to the other two tables like so (assuming the Country table/query is called Country):
SELECT Country.Country, Query1.Quantity, Query2.quantity
FROM Country
LEFT OUTER JOIN Query1 ON Query1.Country = Country.Country
LEFT OUTER JOIN Query2 ON Country.Country = Query2.Country

MS Access SQL, summing a few fields and comparing the value

My table called lets say "table1" looks as follows:
Area | Owner | Numberid | Average
1200 | Fed_G | 998 | 1400
1220 | Priv | 1001 | 1600
1220 | Local_G | 1001 | 1430
1220 | Prov_G | 1001 | 1560
1220 | Priv | 1674 | 1845
1450 | Prov_G | 1874 | 1982
Ideally what I would like to do is sum a few rows in the average column if:
1. they have the same numberid (lets say three rows all had a numberid=1000 then they would have their average added together)
2.Area=1220
Then take that and append it to the existing table, while setting the Owner field equal to "ALL".
I just started working with Access so I'm not really sure how to do this, this is my horrible attempt:
SELECT ind.Area, ind.Owner, ind.numberid,
(SELECT SUM([Average]) FROM [table1]
WHERE [numberid]=ind.numberid) AS Average
FROM [table1] AS ind
WHERE (((ind.numberid)>="1000" And (ind.numberid)<"10000") AND ((ind.Area)="1220"))
Can anyone guide me through what I should do? I'm not used to sql syntax.
I tried to use "ind" as a variable to compare to.
So far it gives me column names but no output.
I'm still unsure whether I understand what you want. So I'll offer you this query and let you tell us if the result is what you want or how it differs from what you want.
SELECT
t.Area,
'ALL' AS Owner,
t.Numberid,
Sum(t.Average) AS SumOfAverage
FROM table1 AS t
GROUP BY t.Area, 'ALL', t.Numberid;
Using your sample data, that query gives me this result set.
Area Owner Numberid SumOfAverage
1200 ALL 998 1400
1220 ALL 1001 4590
1220 ALL 1674 1845
1450 ALL 1874 1982
Probably I would be able to (maybe) give you a better answer if you improve the wording of your question.
However, to <> you need to select average column and group by numberid and Area columns. Since the Owner field is <> I guess it doesn't matter in this query that I'm writing:
SELECT numberid, area, SUM(average)
FROM table1
WHERE owner = 'your-desired-owner-equal-to-all'
GROUP BY numberid, area
ORDER BY numberid, area