How to GROUP BY a new index in a new VIEW - sql

I have 2 tables (load+road) and I want to make a new view with creating new column (flag) that indexate the COUNT of the lines, and then GROUP BY this new index.
I have tried this: (but it doesnt work)
sprintf(my_cmd,
"CREATE VIEW myVIEW(id, Rlength, Llength, flag) AS "
"SELECT road.id, road.length, load.length, COUNT(*) AS flag
FROM road, load "
"WHERE road.id=load.id; "
"SELECT id, Rlength, Llength
FROM myVIEW"
"GROUP BY flag");
ERROR:
Error executing query: ERROR: column "road.id" must appear in the GROUP BY clause or be used in an aggregate function
I am using MY SQL.
*edit:
I dont want that the new column (flag) appears in the last SELECT, but I want to group by it.. dont know if it can be done. if not, the thing I wanna reach, is to use group by on "SELECT id, Rlength, Llength " and to get all the lines in an only one group, but I dont have a Common parameter between thees lines so I have trying to add this "flag"
the full code (sorry for long question):
sprintf(my_cmd,
"CREATE VIEW myVIEW3(id, Rlength, Llength, flag) AS"
" SELECT road.id, road.length, load.length, COUNT(*) AS flag
FROM road, load"
" WHERE road.id=load.id;"
" SELECT id, Rlength, Llength FROM myVIEW3"
" GROUP BY flag"
" HAVING COUNT(*) <= %d"
" ORDER BY (CAST(Llength AS float) / CAST(Rlength AS float)) DESC, id DESC",k);
and what I am trying to do, is to get first k lines after making some ORDER without using LIMIT/TOP (its an assigment). So I have tried using new VIEW with some indecator that I will use for grouping all lines into one group and then use HAVING COUNT(flag) <= k.
road:
.--------.----------------.----------------.
| Id | length | speed |
.--------.----------------.----------------.-
| 9 | 55 | 90 |
| 10 | 44 | 80 |
| 11 | 70 | 100 |
load:
.--------.----------------.----------------.
| Id | length | speed |
.--------.----------------.----------------.-
| 9 | 10 | 20 |
| 10 | 15 | 30 |
| 11 | 30 | 60 |
COMMAND:
loadRanking 2
(k=2, so I want to get first 2 lines after some ORDER, lets not talk about the ORDER in this result)
result:
.--------.----------------.----------------.
| Id | length | speed |
.--------.----------------.----------------.-
| 9 | 10/55 | 20/90 |
| 10 | 15/44 | 30/80 |

Your group by should contain all columns that are being selected that are not part of the aggregate function. So your GROUP BY should look like this:
GROUP BY road.id, road.length, load.length
That being said, I am quite confused by why you have two queries here. I suspect your query should look something like this:
SELECT road.id, road.length, load.length, COUNT(*) AS flag
FROM road, load
WHERE road.id=load.id
GROUP BY road.id, road.length, load.length
HAVING COUNT(*) <= %d
ORDER BY (CAST(load.length AS float) / CAST(road.length AS float)) DESC, road.id DESC
The GROUP BY Statement
Additional note: Try making sure your query works before making it into a view.

Related

Postgresql: Dynamic Regex Pattern

I have event data that looks like this:
id | instance_id | value
1 | 1 | a
2 | 1 | ap
3 | 1 | app
4 | 1 | appl
5 | 2 | b
6 | 2 | bo
7 | 1 | apple
8 | 2 | boa
9 | 2 | boat
10 | 2 | boa
11 | 1 | appl
12 | 1 | apply
Basically, each row is a user typing a new letter. They can also delete letters.
I'd like to create a dataset that looks like this, let's call it data
id | instance_id | value
7 | 1 | apple
9 | 2 | boat
12 | 1 | apply
My goal is to extract all the complete words in each instance, accounting for deletion as well - so it's not sufficient to just get the longest word or the most recently typed.
To do so, I was planning to do a regex operation like so:
select * from data
where not exists (select * from data d2 where d2.value ~ (d.value || '.'))
Effectively I'm trying to build a dynamic regex that adds matches one character more than is present, and is specific to the row it's matching against.
The code above doesn't seem to work. In Python, I can "compile" a regex pattern before I use it. What is the equivalent in PostgreSQL to dynamically build a pattern?
Try simple LIKE operator instead of regex patterns:
SELECT * FROM data d1
WHERE NOT EXISTS (
SELECT * FROM data d2
WHERE d2.value LIKE d1.value ||'_%'
)
Demo: https://dbfiddle.uk/?rdbms=postgres_9.6&fiddle=cd064c92565639576ff456dbe0cd5f39
Create an index on value column, this should speed up the query a bit.
To find peaks in the sequential data window functions is a good choice. You just need to compare each value with previous and next ones using lag() and lead() functions:
with cte as (
select
*,
length(value) > coalesce(length(lead(value) over (partition by instance_id order by id)),0) and
length(value) > coalesce(length(lag(value) over (partition by instance_id order by id)),length(value)) as is_peak
from data)
select * from cte where is_peak order by id;
Demo

Access query to grab +5 or more duplicates

i have a little problem with an Access query ( dont ask me why but i cannot use a true SGBD but Access )
i have a huge table with like 920k records
i have to loop through all those data and grab the ref that occur more than 5 time on the same date
table = myTable
--------------------------------------------------------------
| id | ref | date | C_ERR_ANO |
--------------------------------------------|-----------------
| 1 | A12345678 | 2012/02/24 | A 4565 |
| 2 | D52245708 | 2011/05/02 | E 5246 |
| ... | ......... | ..../../.. | . .... |
--------------------------------------------------------------
so to resume it a bit, i have like 900000+ records
there is duplicates on the SAME DATE ( oh by the way there is another collumn i forgot to add that have C_ERR_ANO as name)
so i have to loop through all those row, grab each ref based on date AND errorNumber
and if there is MORE than 5 time with the same errorNumber i have to grab them and display it in the result
i ended up using this query:
SELECT DISTINCT Centre.REFERENCE, Centre.DATESE, Centre.C_ERR_ANO
FROM Centre INNER JOIN (SELECT
Centre.[REFERENCE],
COUNT(*) AS `toto`,
Centre.DATESE
FROM Centre
GROUP BY REFERENCE
HAVING COUNT(*) > 5) AS Centre_1
ON Centre.REFERENCE = Centre_1.REFERENCE
AND Centre.DATESE <> Centre_1.DATESE;
but this query isent good
i tried then
SELECT DATESE, REFERENCE, C_ERR_ANO, COUNT(REFERENCE) AS TOTAL
FROM (
SELECT *
FROM Centre
WHERE (((Centre.[REFERENCE]) NOT IN (SELECT [REFERENCE]
FROM [Centre] AS Tmp
GROUP BY [REFERENCE],[DATESE],[C_ERR_ANO]
HAVING Count(*)>1 AND [DATESE] = [Centre].[DATESE]
AND [C_ERR_ANO] = [Centre].[C_ERR_ANO]
AND [LIBELLE] = [Centre].[LIBELLE])))
ORDER BY Centre.[REFERENCE], Centre.[DATESE], Centre.[C_ERR_ANO])
GROUP BY REFERENCE, DATESE, C_ERR_ANO
still , not working
i'm struggeling
Your group by clause needs to include all of the items in your select. Why not use:
select Centre.DATESE, Centre.C_ERR_ANO, Count (*)
Group by Centre.DATESE, Centre.C_ERR_ANO
HAVING COUNT (*) > 5
If you need other fields then you can add them, as long as you ensure the same fields appear in the select as the group by.
No idea what is going on with the formatting here!

Select only partial result but get total number of rows

I got stuck on SQL subquery selection. Right now, I have a table products:
id | name | description
----+-------+----------------------
6 | 123 | this is a +
| | girl.
7 | 124 | this is a +
| | girl.
8 | 125 | this is a +
| | girl.
9 | 126 | this is a +
| | girl.
10 | 127 | this is a +
| | girl.
11 | 127 | this is a +
| | girl. Isn't this?
12 | 345 | this is a cute slair
13 | ggg | this is a +
| | girl
14 | shout | this is a monster
15 | haha | they are cute
16 | 123 | this is cute
What I want to do is to find ( the total number of records and the first 5 records ) which contains '1' or 'this' in either name or description columns.
What I can figure out is so ugly:
SELECT *, (select count(id)
from (SELECT * from products
where description like any (array['%1%','%this%'])
or name like any (array['%1%','%this%'])
) as foo
) as total
from (SELECT * from products
where description like any (array['%1%','%this%'])
or name like any (array['%1%','%this%']))
) as fooo
limit 5;
You can use the aggregate function count() as window function to compute the total count in the same query level:
SELECT id, name, description, count(*) OVER () AS total
FROM products p
WHERE description LIKE ANY ('{%1%,%this%}'::text[])
OR name LIKE ANY ('{%1%,%this%}'::text[])
ORDER BY id
LIMIT 5;
Quoting the manual on window functions:
In addition to these functions, any built-in or user-defined aggregate function can be used as a window function
This works, because LIMIT is applied after window functions.
I also use an alternative syntax for array literals. One is as good as the other. This one is shorter for longer arrays. And sometimes an explicit type cast is needed. I am assuming text here.
It is simpler and a bit faster than the version with a CTE in my test.
BTW, this WHERE clause with a regular expression is shorter - but slower:
WHERE description ~ '(1|this)'
OR name ~ '(1|this)'
Ugly, but fast
One more test: I found the primitive version (similar to what you had already) to be even faster:
SELECT id, name, description
, (SELECT count(*)
FROM products p
WHERE description LIKE ANY ('{%1%,%this%}'::text[])
OR name LIKE ANY ('{%1%,%this%}'::text[])
) AS total
FROM products p
WHERE description LIKE ANY ('{%1%,%this%}'::text[])
OR name LIKE ANY ('{%1%,%this%}'::text[])
ORDER BY id
LIMIT 5;
Assuming you are using postgresql 9.0+, you can use CTE's for this.
Eg.
WITH p AS (
SELECT *
FROM products
WHERE description LIKE ANY (ARRAY['%1%','%this%']) OR name LIKE ANY (ARRAY['%1%','%this%'])
)
SELECT *,
(select count(*) from p) as total
FROM p
ORDER BY id LIMIT 5;

How to display a one to many relationship as one to one

I'm working with a paging system. For some of my co-workers I need to create something of a directory. Each person in the directory has a PAGER_ID and a MESSAGING_ID. The PAGER_ID is unique to a given paging device, while a MESSAGING_ID is unique to a person. A group might work in shifts and pass a single device from shift to shift resulting in several people having an identical PAGER_ID and different MESSAGING_ID. This is fine and by design.
For our directory the users want the following criteria met:
Each PAGER_ID to show up once and only once (I know I could do this by SELECT TOP 1 to the PAGER_ID)
A ten digit MESSAGING_ID is preferred when a PAGER_ID has a MESSAGING_ID that is ten digits and one or more other MESSAGING_ID with a different number of digits.
If there is no 10 digit MESSAGING_ID a MESSAGING_ID with any other number of digits will do.
Succinctly: They want to see only one record for a PAGER_ID / MESSAGING_ID. They don't care if every individual person is listed in the directory. They don't really care which MESSAGING_ID as long as each PAGER_ID shows in the directory with only one MESSAGING_ID and as long as a 10 digit MESSAGING_ID is given preference over those not 10 digits long.
I've tried combinations of TOP and IIF queries and have been unable to get them to all play nice together in the manner needed.
My basic select query is:
SELECT
tbl_Amcom_Prod.NAME,
tbl_Amcom_Prod.PAGER_ID,
tbl_Amcom_Prod.MESSAGING_ID
FROM tbl_Amcom_Prod
WHERE
(((tbl_Amcom_Prod.PAGER_ID) Like "241662"
Or (tbl_Amcom_Prod.PAGER_ID) Like "18888"))
ORDER BY tbl_Amcom_Prod.PAGER_ID;
and results in this:
| NAME | PAGER_ID | MESSAGING_ID |
--------------------------------------------
| TESTER 1 | 18888 | 18888 |
--------------------------------------------
| SMITH, MARK | 18888 | 5735551262 |
--------------------------------------------
| SUPERVISOR | 241662 | 102621 |
--------------------------------------------
| JOHN, JONES | 241662 | 101436 |
--------------------------------------------
| SEEGER, ROBERT | 241662 | 101409 |
--------------------------------------------
They want to see this:
| NAME | PAGER_ID | MESSAGING_ID |
--------------------------------------------
| SMITH, MARK | 18888 | 5735551262 |
--------------------------------------------
| SUPERVISOR | 241662 | 102621 |
--------------------------------------------
Any ideas?
If PAGER_ID and MESSAGING_ID are both text data type, this query returns the results you requested.
SELECT
t.NAME,
t.PAGER_ID,
t.MESSAGING_ID
FROM
(
SELECT
sub1.PAGER_ID,
DLookUp(
"MESSAGING_ID",
"tbl_Amcom_Prod",
"PAGER_ID = '" & sub1.PAGER_ID &
"' AND Len(MESSAGING_ID) = " &
sub1.MESSAGING_ID_max_length
) AS MESSAGING_ID
FROM
(
SELECT
PAGER_ID,
Max(Len(MESSAGING_ID))
AS MESSAGING_ID_max_length
FROM tbl_Amcom_Prod
GROUP BY PAGER_ID
) AS sub1
) AS sub2
INNER JOIN tbl_Amcom_Prod AS t
ON sub2.MESSAGING_ID = t.MESSAGING_ID
WHERE t.PAGER_ID In ("241662","18888")
ORDER BY t.PAGER_ID;
If PAGER_ID is Long Integer instead of text, use ...
DLookUp(
"MESSAGING_ID",
"tbl_Amcom_Prod",
"PAGER_ID = " & sub1.PAGER_ID &
" AND Len(MESSAGING_ID) = " &
sub1.MESSAGING_ID_max_length
) AS MESSAGING_ID
and ...
WHERE t.PAGER_ID In (241662,18888)
If MESSAGING_ID is Long Integer instead of text, change Max(Len(MESSAGING_ID)) to Max(Len(CStr(MESSAGING_ID))) and Len(MESSAGING_ID) to Len(Cstr(MESSAGING_ID))
Perhaps:
SELECT tt.NAME, tt.PAGER_ID, tt.MESSAGING_ID
FROM tt
WHERE tt.PAGER_ID In (
SELECT [PAGER_ID]
FROM tt a
WHERE a.MESSAGING_ID IN (
SELECT TOP 1 MESSAGING_ID
FROM tt
ORDER BY Len(MESSAGING_ID) DESC,PAGER_ID))
Where tt is your table.
Ok lets say your table is MyTable, then all you need is to group:
SELECT FIRST(Name), PAGER_ID, MAX(MESSAGING_ID)
FROM MyTable
GROUP BY PAGER_ID

Row Rank in a MySQL View

I need to create a view that automatically adds virtual row number in the result. the graph here is totally random all that I want to achieve is the last column to be created dynamically.
> +--------+------------+-----+
> | id | variety | num |
> +--------+------------+-----+
> | 234 | fuji | 1 |
> | 4356 | gala | 2 |
> | 343245 | limbertwig | 3 |
> | 224 | bing | 4 |
> | 4545 | chelan | 5 |
> | 3455 | navel | 6 |
> | 4534345| valencia | 7 |
> | 3451 | bartlett | 8 |
> | 3452 | bradford | 9 |
> +--------+------------+-----+
Query:
SELECT id,
variety,
SOMEFUNCTIONTHATWOULDGENERATETHIS() AS num
FROM mytable
Use:
SELECT t.id,
t.variety,
(SELECT COUNT(*) FROM TABLE WHERE id < t.id) +1 AS NUM
FROM TABLE t
It's not an ideal manner of doing this, because the query for the num value will execute for every row returned. A better idea would be to create a NUMBERS table, with a single column containing a number starting at one that increments to an outrageously large number, and then join & reference the NUMBERS table in a manner similar to the variable example that follows.
MySQL Ranking, or Lack Thereof
You can define a variable in order to get psuedo row number functionality, because MySQL doesn't have any ranking functions:
SELECT t.id,
t.variety,
#rownum := #rownum + 1 AS num
FROM TABLE t,
(SELECT #rownum := 0) r
The SELECT #rownum := 0 defines the variable, and sets it to zero.
The r is a subquery/table alias, because you'll get an error in MySQL if you don't define an alias for a subquery, even if you don't use it.
Can't Use A Variable in a MySQL View
If you do, you'll get the 1351 error, because you can't use a variable in a view due to design. The bug/feature behavior is documented here.
Oracle has a rowid pseudo-column. In MySQL, you might have to go ugly:
SELECT id,
variety,
1 + (SELECT COUNT(*) FROM tbl WHERE t.id < id) as num
FROM tbl
This query is off the top of my head and untested, so take it with a grain of salt. Also, it assumes that you want to number the rows according to some sort criteria (id in this case), rather than the arbitrary numbering shown in the question.