Get values filtered by jsonb key - sql

I have a table called houses that has a jsonb column called details . The structure of the details jsonb column is:
{
owner_id: TEXT,
owner_name: TEXT,
offers: [{ offer_id: TEXT, offer_value_id: TEXT, offer_value_name: TEXT }]
}
Notice, that sometimes offers can be completely empty, such as offers: []
So, right now I have a query that lets me get all the distinct house owners ordered by owner_name. It looks like this:
SELECT distinct ("details"->>'owner_id') as "identifier", "details"->>'owner_name' as "name"
FROM houses
order by "details"->>'owner_name' asc
I want to do something similar, but now I want to get all the different offer values but just for a specific offer_id. Here is some sample data followed but what I would expect:
id, details
1, { owner_id: '1', owner_name: 'john', offers: [] }
2, { owner_id: '2', owner_name: 'charles', offers: [ { offer_id: '1', offer_value_id: '1', offer_value_name: 'offer1'}, { offer_id: '2', offer_value_id: '2', offer_value_name: 'offer2'}] }
3, { owner_id: '3', owner_name: 'melissa', offers: [ { offer_id: '2', offer_value_id: '5', offer_value_name: 'a offer 3'} ]
4, { owner_id: '3', owner_name: 'melissa', offers: [ { offer_id: '6', offer_value_id: '8', offer_value_name: 'offer10'} ]
So, say I want to get all the different offer value ids and value names when the offer_id is '2'. The result would be:
identifier (this would be offer_value_id), name (this would be offer_value_name)
'5', 'a offer 3'
'2', 'offer2'
null, null
Notice that there is null, null because there is at least two rows that don't have any offers where offer_id is 1, and I want to get that too. Also, notice that the values are ordered by offer_value_name NULLs being last.
I've tried the following but is not working:
SELECT distinct ("details"->>'offers'->>'offer_value_id') as "identifier", ("details"->>'offers'->>'offer_value_name') as "name"
FROM houses
WHERE "details"->>'offers'->>'offer_id' = '2'
order by "details"->>'offers'->>'offer_value_name' asc
And I don't think this approach would work, because if the details offers don't have an offer_id, I also want it to select NULL, this would just filter it out.

I think this would work:
SELECT DISTINCT "offers"->>'offer_value_id' as "identifier", "offers"->>'offer_value_name' as "name"
FROM houses
LEFT JOIN jsonb_array_elements("details"->'offers') "offers" ON "offers"->>'offer_id' = '1';
ORDER BY "offers"->>'offer_value_name' NULLS LAST
You know you want to get all the records regardless of whether that offer with id 1 exists or not, that's why you do a LEFT JOIN.
The other thing to notice here is jsonb_array_elements that is helpful because it expands that json to a set of json values. That way you can access offers as if it were a top level field.

Related

Select all Valid starting letters with Sequelize

I have a list of countries which will be separated by starting letter so for example when you click on 'A' it will make an API call to return all the countries beginning with 'A'.
However there are some letters that don't have any countries in our system, and these may change as we update out data.
I want to have a query that will let me know which letters do not have any countries that begin with them, so that I can disable them.
I can do this be running a findOne query for every single letter in the alphabet... but that is not neat or performant. Is there a way to get the data from a single query?
I am able to get the desired result by using a substring function within a distinct function.
const result = await Countries.findAll({
attributes: [
[
sequelize.fn(
'DISTINCT',
sequelize.fn('substring', sequelize.col('countryName'), 1, 1),
),
'letter',
],
],
group: [sequelize.fn('substring', sequelize.col('countryName'), 1, 1)],
raw: true,
})

How to write a Kusto query to find two consecutive rows that have the same value in a field

I need to write a Kusto query for Azure Log Analysis that finds consecutive events that have the same value in a field (same error code). We basically need to find if the requests fail twice in a row.
The case where a request fails, one succeeds and one fails is not to be returned.
Assuming you have a table with Id, Datetime, and a ErrorCode, you can utilize prev() function to achieve this:
https://learn.microsoft.com/en-us/azure/kusto/query/prevfunction
datatable(Id:string, Datetime:datetime, ErrorCode:string)
[
'1', datetime(2018-10-16 00:00), 'Error 1',
'1', datetime(2018-10-16 00:01), 'Error 1',
'2', datetime(2018-10-16 00:02), 'Error 1',
'2', datetime(2018-10-16 00:03), 'Error 2',
]
| order by Id, Datetime asc
| extend prevErrorCode = prev(ErrorCode), prevId=prev(Id)
| where prevErrorCode==ErrorCode and prevId == Id

Is it possible to combine data from different tables in one column

I'm trying to achieve the following JSON structure with a SQL-Schema:
"data_set": [
{
"id": 0,
"annotation": "foo",
"value":
{
"type": "number",
"value": 10.0,
"unit": "m"
}
},
{
"id": 1,
"annotation": "bar",
"value":
{
"type": "text",
"value": "Hello World"
}
}
]
The tricky part is that I not only want to includes values of one type, but different types. My thought was to have different tables for each value e.g.:
numeric_value: id {PK} | type | value | unit
text_values: id {PK} | type | value
and include them in the data_set table via foreign key:
data_set: id {PK} | annotation | value {FK}
My problem is that I'm not sure how to reference ids from different tables in one column using keys, of if I'm taking a totally wrong approach in tackling this problem.
You can do this in different ways. One of them is to use only one table, and have it defined in such a way that, when your value_type is number you are forced to fill in the values for numeric_value and unit (and nothing else), and when it is text, you must then fill in the text_value column (and not fill in the rest, but leave NULL).
This definition could do it:
CREATE TABLE data_set
(
data_set_id integer PRIMARY KEY,
annotation text,
value_type text NOT NULL,
numeric_value numeric,
unit text,
text_value text,
/* Constraint to make sure values are consistent with definition */
CHECK (CASE WHEN value_type = 'text' THEN
numeric_value IS NULL AND unit IS NULL AND text_value IS NOT NULL
WHEN value_type = 'number' THEN
numeric_value IS NOT NULL AND unit IS NOT NULL AND text_value IS NULL
ELSE
false
END)
) ;
The following inserts would be valid:
INSERT INTO
data_set
(data_set_id, annotation, value_type, numeric_value, unit)
VALUES
(1, 'height', 'number', 10.0, 'm') ;
INSERT INTO
data_set
(data_set_id, annotation, value_type, text_value)
VALUES
(2, 'brand', 'text', 'BigBrand') ;
These ones would not:
INSERT INTO
data_set
(data_set_id, annotation, value_type, text_value)
VALUES
(3, 'brand', 'strange_type', 'misstake') ;
INSERT INTO
data_set
(data_set_id, annotation, value_type, text_value)
VALUES
(4, 'brand', 'number', 'misstake') ;

Kohana ORM ordering

I'm using Kohana's ORM library, and I'm wondering if there is any way to select DB records in a particular predetermined sequence.
$products_ids = array('5', '6', '1', '33', '2');
$products = ORM::factory('Product')->where('state', '=', 1)
->and_where('id', 'IN', $products_ids)->find_all();
This orders result by primary key (id). So result records ordered like (1, 2, 5, 6, 33). How select records by order defined in $products_ids ('5', '6', '1', '33', '2')?
Thanks.
Yes this is possible.
$products = ORM::factory('Product')->where('state', '=', 1)
->and_where('id', 'IN', $products_ids)->order_by('product_id', 'desc')->find_all();
Note the added order_by() in the string above.
You can order your results now. It has more cool features like group_by().
Read the documentation and you will find more unexpected magic in ORM.
In MySQL you can have conditions in order by clause where if the condition match it gets treated as 1 and 0 on fails.
The code for the example would be:
$products_ids = array('5', '6', '1', '33', '2');
$products = ORM::factory('Product')->where('state', '=', 1)->and_where('id', 'IN', $products_ids);
foreach($products_ids as $product_id)
{
$products->order_by(DB::expr('id='.$product_id), 'desc');
}
$products = $products->find_all();
In MySQL it would look something like this:
SELECT * FROM products WHERE ... ORDER BY id=5 DESC, id=6 DESC, id=1 DESC, id=33 DESC, id=2 DESC;
I don't know why you wan do so stupid thing, but 2 answers you have. (For stric problem. But probably you don't show full background.
I propose 2 scenarios:
Products on page category - add column for order it to table.
Display shopping chart - order by order_item_id
BTW: ORM is comfortable, but slow, so if you nedd it only for display (read operation) better is using raw query. Here solution for ORM.
public function get_ordered(array $ids){
if(empty($ids))
return array();
$res = DB::select->from($this->_table_name)
->where($this->primary_key(),'IN',$ids)
->execute($this->_db)->as_array($this->primary_key());
$out = array();
foreach($ids AS $one){
if(isset($res[$one]))
$out[$one] = $res[$one];
}
return $out;
}

SQL server query on json string for stats

I have this SQL Server database that holds contest participations. In the Participation table, I have various fields and a special one called ParticipationDetails. It's a varchar(MAX). This field is used to throw in all contest specific data in json format. Example rows:
Id,ParticipationDetails
1,"{'Phone evening': '6546546541', 'Store': 'StoreABC', 'Math': '2', 'Age': '01/01/1951'}"
2,"{'Phone evening': '6546546542', 'Store': 'StoreABC', 'Math': '2', 'Age': '01/01/1952'}"
3,"{'Phone evening': '6546546543', 'Store': 'StoreXYZ', 'Math': '2', 'Age': '01/01/1953'}"
4,"{'Phone evening': '6546546544', 'Store': 'StoreABC', 'Math': '3', 'Age': '01/01/1954'}"
I'm trying to get a a query runing, that will yield this result:
Store, Count
StoreABC, 3
StoreXYZ, 1
I used to run this query:
SELECT TOP (20) ParticipationDetails, COUNT(*) Count FROM Participation GROUP BY ParticipationDetails ORDER BY Count DESC
This works as long as I want unique ParticipationDetails. How can I change this to "sub-query" into my json strings. I've gotten to this query, but I'm kind of stuck here:
SELECT 'StoreABC' Store, Count(*) Count FROM Participation WHERE ParticipationDetails LIKE '%StoreABC%'
This query gets me the results I want for a specific store, but I want the store value to be "anything that was put in there".
Thanks for the help!
first of all, I suggest to avoid any json management with t-sql, since is not natively supported. If you have an application layer, let it to manage those kind of formatted data (i.e. .net framework and non MS frameworks have json serializers available).
However, you can convert your json strings using the function described in this link.
You can also write your own query which works with strings. Something like the following one:
SELECT
T.Store,
COUNT(*) AS [Count]
FROM
(
SELECT
STUFF(
STUFF(ParticipationDetails, 1, CHARINDEX('"Store"', ParticipationDetails) + 9, ''),
CHARINDEX('"Math"',
STUFF(ParticipationDetails, 1, CHARINDEX('"Store"', ParticipationDetails) + 9, '')) - 3, LEN(STUFF(ParticipationDetails, 1, CHARINDEX('"Store"', ParticipationDetails) + 9, '')), '')
AS Store
FROM
Participation
) AS T
GROUP BY
T.Store