Using 'NOT' operator in Sphinx Search? - sphinx

I have a index 'order' with field id, date, and status. I need to get all order not having status CLOSED.
My sphinx Query is :
SELECT id, status FROM order
WHERE MATCH('#status "!CLOSED"') AND id < 21;
+------+-------------+
| id | status |
+------+-------------+
| 10 | CLOSED |
| 1 | CLOSED |
| 4 | CLOSED |
| 5 | CLOSED |
| 7 | CLOSED |
| 9 | CLOSED |
| 14 | CLOSED |
| 18 | CLOSED |
| 19 | CLOSED |
+------+-------------+
9 rows in set (0.06 sec)
Where I went wrong? If I want to do NOT in multiple field how to do that?
Can you please rewrite this SphinxQL for me?

You've put the term in phrase operators, so its looking for the phrase !CLOSED - but as I doubt that you have ! in your charset_table its ignored, so becomes just a search for CLOSED.
... WHERE MATCH('#status !CLOSED')
would in theory work. But sphinx can't execute a query with just 'NOT' terms.
Sphinx has an inverted index, listing which words are in which documents. It doesnt have a list of all documents that it can then 'remove' specific ones.
... so you need a list of all documents. Easiest is to add a fake keyword to all documents, eg modify your source definition (in sphinx.conf) to add a fake field something like...
sql_query = SELECT id,...,'_all' as fake FROM sqltable ...
Then can using it in your sphinx queries....
... WHERE MATCH('_all #status -CLOSED')

Related

SQL parameter table

I suspect this question is already well-answered but perhaps due to limited SQL vocabulary I have not managed to find what I need. I have a database with many code:description mappings in a single 'parameter' table. I would like to define a query or procedure to return the descriptions for all (or an arbitrary list of) coded values in a given 'content' table with their descriptions from the parameter table. I don't want to alter the original data, I just want to display friendly results.
Is there a standard way to do this?
Can it be accomplished with SELECT or are other statements required?
Here is a sample query for a single coded field:
SELECT TOP (5)
newid() as id,
B.BRIDGE_STATUS,
P.SHORTDESC
FROM
BRIDGE B
LEFT JOIN PARAMTRS P ON P.TABLE_NAME = 'BRIDGE'
AND P.FIELD_NAME = 'BRIDGE_STATUS'
AND P.PARMVALUE = B.BRIDGE_STATUS
ORDER BY
id
I want to produce 'decoded' results like:
| id | BRIDGE_STATUS |
|--------------------------------------|------------ |
| BABCEC1E-5FE2-46FA-9763-000131F2F688 | Active |
| 758F5201-4742-43C6-8550-000571875265 | Active |
| 5E51634C-4DD9-4B0A-BBF5-00087DF71C8B | Active |
| 0A4EA521-DE70-4D04-93B8-000CD12B7F55 | Inactive |
| 815C6C66-8995-4893-9A1B-000F00F839A4 | Proposed |
Rather than original, coded data like:
| id | BRIDGE_STATUS |
|--------------------------------------|---------------|
| F50214D7-F726-4996-9C0C-00021BD681A4 | 3 |
| 4F173E40-54DC-495E-9B84-000B446F09C3 | 3 |
| F9C216CD-0453-434B-AFA0-000C39EFA0FB | 3 |
| 5D09554E-201D-4208-A786-000C537759A1 | 1 |
| F0BDB9A4-E796-4786-8781-000FC60E200C | 4 |
but for an arbitrary number of columns.

Create calculated fields in crosstab

I have a crosstab between supplier and order acceptance status, containing maxvalue of a number.
I need to create a formula like :
IF ACCEPTED > MISSING THEN "GOOD" ELSE "BAD"
Can you help with the syntax?
This is what I get using formula suggested and this is what I should get
ORDER | ACCEP | MISSING | SHOULDBE | NOW |
-------------------------------------------------------------------------------
61010 | 6 | 0 | GOOD | GOOD | FORMULAOK
61011 | 3 | 12 | BAD | BAD | FORMULAOK
63239 | 9 | 11 | BAD | BAD | FORMULAOK
66749 | 0 | | BAD | GOOD | FORMULAnotOK
76824 | 2 | 1 | GOOD | BAD | FORMULAnotOK
Use a SUM() and INT() function to do Conditional Checks ,convert Boolean as Numbers and add the numbers to get total counts. Then its just a comparison.
IF SUM(INT([ACCEPTANCESTATUS]="ACCEPTED")) > SUM(INT([ACCEPTANCESTATUS]="MISSING"))
THEN "GOOD" ELSE "BAD" END
You're data is already pivoted, you can just write a calculated field exactly has you framed your pseudo code:
IF SUM([ACCEP]) > SUM([MISSING]=)
THEN "GOOD"
ELSE "BAD"
END
You can remove the sum elements if you want the value calculated for every row (and not aggregated).

Tableau - Show multiple discrete string (dropdown) dimensions side-by-side in a single table

I have a list of survey results that looks similar to the following:
| Email | Question 1 | Question 2 |
| ----------------- | ---------- | ---------- |
| test#example.com | Always | Sometimes |
| test2#example.com | Always | Always |
| test3#example.com | Sometimes | Never |
Question 1 and Question 2 (and a few others) have the same discrete set of values (from a dropdown list on the survey).
I want to show the data in the following format in Tableau (a table is fine, but a heatmap or highlight table would be best):
| | Always | Sometimes | Never |
| ---------- | ------ | --------- | ----- |
| Question 1 | 2 | 1 | 0 |
| Question 2 | 1 | 1 | 1 |
How can I achieve this? I've tried various combinations of rows and columns and I just can't seem to get close to this layout. Do I need to use a calculated value?
As far as I know - it is not natively possible with Tableau, because what you have is kind of a pivot table.
What you can do is unpivot the whole table as explained here https://stackoverflow.com/a/20543651/5130012, then you can load the data into Tableau and create the table you want.
I did some dummy data and tried it.
That's my "unpivoted" table:
Row,Column,Value
test,q1,always
test,q2,sometimes
test1,q1,sometimes
test1,q2,never
test10,q1,always
test10,q2,always
test11,q1,sometimes
test11,q2,never
And that's how it looks in Tableau:

PostgreSQL simple count query

Trying to scale this down so the answer is simple. I can probably extrapolate the answers here to apply to a bigger data set.
Given the following table:
+------+-----+
| name | age |
+------+-----+
| a | 5 |
| b | 7 |
| c | 8 |
| d | 8 |
| e | 10 |
+------+-----+
I want to make a table that shows the count of people where their age is equal to or greater than x. For instance, the table about would produce:
+--------------+-------+
| at least age | count |
+--------------+-------+
| 5 | 5 |
| 6 | 4 |
| 7 | 4 |
| 8 | 3 |
| 9 | 1 |
| 10 | 1 |
+--------------+-------+
Is there a single query that can accomplish this task? Obviously, it is easy to write a simple function for it, but I'm hoping to be able to do this quickly with one query.
Thanks!
Yes, what you're looking for is a window function.
with cte_age_count as (
select age,
count(*) c_star
from people
group by age)
select age,
sum(c_star) over (order by age
range between unbounded preceding
and current row)
from cte_age_count
Not syntax checked ... let me know if it works!

PostgreSQL - Conditional aggregation - Avg() in Select statement

I have this table
| user | Mark | Points |
|--------------|------------|----------|
| John | 0 | 2 |
| Paul | 5 | 3 |
| John | 4 | 4 |
| Paul | 7 | 5 |
I would like to build a query with one select statement that returns the rows shown below.
Avg(Mark) - should be average only if Mark>0
Sum(Points) - should be sum of all records.
| user | Avg(Mark) | Sum(Points) |
|--------------|------------|-------------|
| John | 4 | 6 |
| Paul | 6 | 8 |
Can anyone point to a proper syntax?
I believe it should like :
select user, avg(Mark>0), sum(Points) from Table group by user;
Starting with version 9.4, PostgreSQL directly supports filtering aggregates.
https://www.postgresql.org/docs/9.4/static/sql-expressions.html
If FILTER is specified, then only the input rows for which the filter_clause evaluates to true are fed to the aggregate function; other rows are discarded.
By using it, your example can be rewritten as:
SELECT
"user",
AVG(mark) FILTER (WHERE mark > 0),
SUM(points)
FROM
"table"
GROUP BY
"user"
How about:
select user,
avg(case when mark > 0 then mark end),
sum(mark)
from ...
select
user, -- very bad choice for column name, but i assume it's just SO example, not real column
sum( mark ) / count (nullif(mark, 0))
from
table
group by
user
should so the trick.