How do I combine AND and OR using Sequel? - sql

I want to generate a SQL query like the following using Ruby's Sequel:
SELECT * FROM Objects WHERE (color = "red" AND shape = "triangle") OR
(color = "blue" AND shape = "square") OR
(color = "green" AND shape = "circle")
I want to build this query programmatically from a list of conditions, so that I can do something like this:
conditions = [[[:color, "red"], [:shape, "triangle"]],
[[:color, "blue"], [:shape, "square"]],
[[:color, "green"], [:shape, "circle"]]]
DB[:Users].where(conditions.sql_or)
It doesn't have to follow that exact form, but I want to be able to build the conditions programmatically, so it's not sufficient to be able to construct this query by hand.

Try this:
conditions = [
{:color => "red", :shape => "triangle"},
{:color => "blue", :shape => "square"},
{:color => "green", :shape => "circle"}
]
head, *tail = *conditions
tail.inject(DB[:Users].filter(head)){|mem,obj| mem.or(obj) }
I get:
=> #<Sequel::Postgres::Dataset: "SELECT * FROM \"Users\" WHERE (((\"color\" = 'red') AND (\"shape\" = 'triangle')) OR ((\"color\" = 'blue') AND (\"shape\" = 'square')) OR ((\"color\" = 'green') AND (\"shape\" = 'circle')))">

I think this will return an equivalent result, using a different SQL query:
DB[:Objects].where('(color, shape) in ?', conditions.sql_value_list).sql
=> "SELECT * FROM `Objects` WHERE ((color, shape) in (('red', 'triangle'), ('blue', 'square'), ('green', 'circle')))"
sql_value_list is documented in http://sequel.rubyforge.org/rdoc/classes/Array.html
Otherwise use:
objects = DB[:Objects].where(conditions[0])
conditions[1 .. -1].each { |c| objects = objects.or(c) }
Which results in:
SELECT * FROM `Objects` WHERE (((`color` = 'red') AND (`shape` = 'triangle')) OR ((`color` = 'blue') AND (`shape` = 'square')) OR ((`color` = 'green') AND (`shape` = 'circle')))
I was looking at Iain's answer, and it's basically the same as my second one, only more succinct; I like its elegance.

Related

Erlang ets:select sublist

Is there a way in Erlang to create a select query on ets table, which will get all the elements that contains the searched text?
ets:select(Table,
[{ %% Match spec for select query
{'_', #movie_data{genre = "Drama" ++ '_' , _ = '_'}}, % Match pattern
[], % Guard
['$_'] % Result
}]) ;
This code gives me only the data that started (=prefix) with the required text (text = "Drama"), but the problem is I need also the the results that contain the data, like this example:
#movie_data{genre = "Action, Drama" }
I tried to change the guard to something like that -
{'_', #movie_data{genre = '$1', _='_'}}, [string:str('$1', "Drama") > 0] ...
But the problem is that it isn't a qualified guard expression.
Thanks for the help!!
It's not possible. You need to design your data structure to be searchable by the guard expressions, for example:
-record(movie_data, {genre, name}).
-record(genre, {comedy, drama, action}).
example() ->
Table = ets:new('test', [{keypos,2}]),
ets:insert(Table, #movie_data{name = "Bean",
genre = #genre{comedy = true}}),
ets:insert(Table, #movie_data{name = "Magnolia",
genre = #genre{drama = true}}),
ets:insert(Table, #movie_data{name = "Fight Club",
genre = #genre{drama = true, action = true}}),
ets:select(Table,
[{#movie_data{genre = #genre{drama = true, _ = '_'}, _ = '_'},
[],
['$_']
}]).

Ruby on Rails value by association

I have this freeform SQL that when run returns the name of a product, name of the associate attribute and the value for that attribute. What I am trying to work out is how I can use the with Ruby on Rails and Active Record
Freeform Query
select menu_items.name, menu_attributes.name, menu_attributes_items.menu_attribute_value from menu_attributes, menu_items, menu_attributes_items, venues
where menu_items.id = menu_attributes_items.menu_item_id
and menu_attributes.id = menu_attributes_items.menu_attribute_id
and venues.id = menu_attributes_items.venue_id
and menu_attributes.name = 'Price'
This is my current HAMl Template, though it only returns a list of the attributes i.e. Price, Size and not the values like the SQL does
- for item in #venue.menu_items
.grid-content
.card
= image_tag("drinks/#{item.name.titlecase.tr(' ','-')}.png")
%img{:src => "http://placehold.it/400x200?text=Coffee"}
.card-divider
= item.name
.card-section
%h4
- for attr in item.menu_attributes
= attr.name
= MenuItem.joins({ menu_attributes: :menu_attributes_items }, :venues).where('menu_items.id = menu_attributes_items.menu_item_id AND menu_attributes.id = menu_attributes_items.menu_attribute_id AND menu_attributes.id = menu_attributes_items.menu_attribute_id AND venues.id = menu_attributes_items.venue_id AND menu_attributes.name = ?', 'Price').select('menu_items.name, menu_attributes.name, menu_attributes_items.menu_attribute_value')
Updates
Price # Size # Size #
Depending on the associations among your models and still with a slight touch of SQL, your query can be written with ActiveRecord this way:
MenuItem.joins({ menu_attributes: :menu_attributes_items }, :venues)
.where('menu_items.id = menu_attributes_items.menu_item_id AND menu_attributes.id = menu_attributes_items.menu_attribute_id AND menu_attributes.id = menu_attributes_items.menu_attribute_id AND venues.id = menu_attributes_items.venue_id AND menu_attributes.name = ?', 'Price')
.select('menu_items.name, menu_attributes.name, menu_attributes_items.menu_attribute_value')

Node-postgres: named parameters query (nodejs)

I used to name my parameters in my SQL query when preparing it for practical reasons like in php with PDO.
So can I use named parameters with node-postgres module?
For now, I saw many examples and docs on internet showing queries like so:
client.query("SELECT * FROM foo WHERE id = $1 AND color = $2", [22, 'blue']);
But is this also correct?
client.query("SELECT * FROM foo WHERE id = :id AND color = :color", {id: 22, color: 'blue'});
or this
client.query("SELECT * FROM foo WHERE id = ? AND color = ?", [22, 'blue']);
I'm asking this because of the numbered parameter $n that doesn't help me in the case of queries built dynamically.
There is a library for what you are trying to do. Here's how:
var sql = require('yesql').pg
client.query(sql("SELECT * FROM foo WHERE id = :id AND color = :color")({id: 22, color: 'blue'}));
QueryConvert to the rescue. It will take a parameterized sql string and an object and converts it to pg conforming query config.
type QueryReducerArray = [string, any[], number];
export function queryConvert(parameterizedSql: string, params: Dict<any>) {
const [text, values] = Object.entries(params).reduce(
([sql, array, index], [key, value]) => [sql.replace(`:${key}`, `$${index}`), [...array, value], index + 1] as QueryReducerArray,
[parameterizedSql, [], 1] as QueryReducerArray
);
return { text, values };
}
Usage would be as follows:
client.query(queryConvert("SELECT * FROM foo WHERE id = :id AND color = :color", {id: 22, color: 'blue'}));
Not exactly what the OP is asking for. But you could also use:
import SQL from 'sql-template-strings';
client.query(SQL`SELECT * FROM unicorn WHERE color = ${colorName}`)
It uses tag functions in combination with template literals to embed the values
I have been working with nodejs and postgres. I usually execute queries like this:
client.query("DELETE FROM vehiculo WHERE vehiculo_id= $1", [id], function (err, result){ //Delete a record in de db
if(err){
client.end();//Close de data base conection
//Error code here
}
else{
client.end();
//Some code here
}
});

How to use ruby .map to add quotes to array elements

See my example below where I put 'something' I'm just confused on what to map:
array_ids = ['1','2']
array = array_ids.map(something).join(',')
So when I do:
order_sql = "FIELD(ID,#{array})"
I get this:
order_sql = "FIELD(ID,'1','2')"
You can do something like:
array_ids = ['1','2']
array = array_ids.map { |id| "'#{id}'" }.join(',')
p array
# => "'1','2'"
order_sql = "FIELD(ID,#{array})"
p order_sql
# => "FIELD(ID,'1','2')"
Hope that helps!

Avoid repeating an expression in a LINQ to Entities query

I have the following query:
val = val.Select(item => new SimpleBill { CTime = item.CTime, Description = item.Description, ID = item.ID,
IDAccount = item.IDAccount, OrderNumber = item.OrderNumber, PSM = item.PSM, Sum = item.Sum,
Type = (BillType)item.Type,
ByteStatus = ((Bill)item).Payments.OrderByDescending(payment => payment.ID).FirstOrDefault().Status,
LastPaymentDate = ((Bill)item).Payments.OrderByDescending(payment => payment.ID).FirstOrDefault().CTime,
LastPaymentSum = ((Bill)item).Payments.OrderByDescending(payment => payment.ID).FirstOrDefault().Sum });
}
Is it possible to avoid repeating the ((Bill)item).Payments.OrderByDescending(payment => payment.ID).FirstOrDefault() part 3 times? I tried turning it into a method and into a delegate - the code compiled in both cases, but produced an exception at runtime.
You can use the let contstruct as follows:
val = from item in val
let lastPayment = ((Bill)item).Payments
.OrderByDescending(payment => payment.ID)
.FirstOrDefault()
select new SimpleBill
{
lastPayment.CTime,
//Rest of fields
}
However, as you may noticed this uses the LINQ Query syntax vs. Method syntax. IIRC let is only available in the former.