Count the size of an array in another column? - sql-server

I'm trying to update "Count_Column" in "My_Table" to get the count of the array size in Array_Column
UPDATE TABLE [dbo1].[My_Table]
SET [Count_Column] as COUNT(Array_Column.test);
Array_Column in a column that has a JSON array in it

It's unclear why you want to store this information a second time, ideally you would just query it when needed.
Be that as it may, you need to break out the array using OPENJSON
UPDATE dbo1.My_Table
SET Count_Column = (
SELECT COUNT(*)
FROM OPENJSON(My_Table.Array_Column, '$.test')
);
This assumes that the array is located in a property called test, for example:
{
"test" : [
1,
2
]
}
would return 2

Related

Return Parts of an Array in Postgres

I have a column (text) in my Postgres DB (v.10) with a JSON format.
As far as i now it's has an array format.
Here is an fiddle example: Fiddle
If table1 = persons and change_type = create then i only want to return the name and firstname concatenated as one field and clear the rest of the text.
Output should be like this:
id table1 did execution_date change_type attr context_data
1 Persons 1 2021-01-01 Create Name [["+","name","Leon Bill"]]
1 Persons 2 2021-01-01 Update Firt_name [["+","cur_nr","12345"],["+","art_cd","1"],["+","name","Leon"],["+","versand_art",null],["+","email",null],["+","firstname","Bill"],["+","code_cd",null]]
1 Users 3 2021-01-01 Create Street [["+","cur_nr","12345"],["+","art_cd","1"],["+","name","Leon"],["+","versand_art",null],["+","email",null],["+","firstname","Bill"],["+","code_cd",null]]
Disassemble json array into SETOF using json_array_elements function, then assemble it back into structure you want.
select m.*
, case
when m.table1 = 'Persons' and m.change_type = 'Create'
then (
select '[["+","name",' || to_json(string_agg(a.value->>2,' ' order by a.value->>1 desc))::text || ']]'
from json_array_elements(m.context_data::json) a
where a.value->>1 in ('name','firstname')
)
else m.context_data
end as context_data
from mutations m
modified fiddle
(Note:
utilization of alphabetical ordering of names of required fields is little bit dirty, explicit order by case could improve readability
resulting json is assembled from string literals as much as possible since you didn't specified if "+" should be taken from any of original array elements
the to_json()::text is just for safety against injection
)

How to delete array element in JSONB column based on nested key value?

How can I remove an object from an array, based on the value of one of the object's keys?
The array is nested within a parent object.
Here's a sample structure:
{
"foo1": [ { "bar1": 123, "bar2": 456 }, { "bar1": 789, "bar2": 42 } ],
"foo2": [ "some other stuff" ]
}
Can I remove an array element based on the value of bar1?
I can query based on the bar1 value using: columnname #> '{ "foo1": [ { "bar1": 123 } ]}', but I've had no luck finding a way to remove { "bar1": 123, "bar2": 456 } from foo1 while keeping everything else intact.
Thanks
Running PostgreSQL 9.6
Assuming that you want to search for a specific object with an inner object of a certain value, and that this specific object can appear anywhere in the array, you need to unpack the document and each of the arrays, test the inner sub-documents for containment and delete as appropriate, then re-assemble the array and the JSON document (untested):
SELECT id, jsonb_build_object(key, jarray)
FROM (
SELECT foo.id, foo.key, jsonb_build_array(bar.value) AS jarray
FROM ( SELECT id, key, value
FROM my_table, jsonb_each(jdoc) ) foo,
jsonb_array_elements(foo.value) AS bar (value)
WHERE NOT bar.value #> '{"bar1": 123}'::jsonb
GROUP BY 1, 2 ) x
GROUP BY 1;
Now, this may seem a little dense, so picked apart you get:
SELECT id, key, value
FROM my_table, jsonb_each(jdoc)
This uses a lateral join on your table to take the JSON document jdoc and turn it into a set of rows foo(id, key, value) where the value contains the array. The id is the primary key of your table.
Then we get:
SELECT foo.id, foo.key, jsonb_build_array(bar.value) AS jarray
FROM foo, -- abbreviated from above
jsonb_array_elements(foo.value) AS bar (value)
WHERE NOT bar.value #> '{"bar1": 123}'::jsonb
GROUP BY 1, 2
This uses another lateral join to unpack the arrays into bar(value) rows. These objects can now be searched with the containment operator to remove the objects from the result set: WHERE NOT bar.value #> '{"bar1": 123}'::jsonb. In the select list the arrays are re-assembled by id and key but now without the offending sub-documents.
Finally, in the main query the JSON documents are re-assembled:
SELECT id, jsonb_build_object(key, jarray)
FROM x -- from above
GROUP BY 1;
The important thing to understand is that PostgreSQL JSON functions only operate on the level of the JSON document that you can explicitly indicate. Usually that is the top level of the document, unless you have an explicit path to some level in the document (like {foo1, 0, bar1}, but you don't have that). At that level of operation you can then unpack to do your processing such as removing objects.

Filter SQL datatable according to different parameters, without a WHERE clause

I'm building an application that needs to allow the user to filter a data table according to different filters. So, the user will have three different filter posibilites but he might use only one, or two or the three of them at the same tame.
So, let's say I have the following columns on the table:
ID (int) PK
Sede (int)
Programa (int)
Estado (int)
All of those columns will store numbers, integers. The "ID" column is the primary key, "Sede" stores 1 or 2, "Programa" is any number between 1 and 15, and "Estado" will store numbers between 1 and 13.
The user may filter the data stored in the table using any of those filters (Sede, Programa or Estado). But the might, as well, use two filters, or the three of them at the same time.
The idea is that this application works like the data filters on Excel. I created a simulated table on excel to show what I want to achieve:
This first image shows the whole table, without applying any filter.
Here, the user selected a filter for "Sede" and "Programa" but leaved the "Estado" filter empty. So the query returns the values that are equal to the filter, but leaves the "Estado" filter open, and brings all the records, filering only by "Sede" (1) and "Programa" (6).
In this image, the user only selected the "Estado" filter (5), so it brings all the records that match this criteria, it doesn't matter if "Sede" or "Programa" are empty.
If I use a SELECT clasuse with a WHERE on it, it will work, but only if the three filters have a value:
DECLARE #sede int
DECLARE #programa int
DECLARE #estado int
SET #sede = '1'
SET #programa = '5'
SET #estado = '12'
SELECT * FROM [dbo].[Inscripciones]
WHERE
([dbo].[Inscripciones].[Sede] = #sede)
AND
([dbo].[Inscripciones].[Programa] = #programa)
AND
([dbo].[Inscripciones].[Estado] = #estado)
I also tryed changing the "AND" for a "OR", but I can't get the desired result.
Any help will be highly appreciated!! Thanks!
common problem: try using coalesce on the variable and for the 2nd value use the field name you're comparing to. Be careful though; Ensure it's NULL and not empty string being passed!
What this does is take the first non-null value of the variable passed in or the value you're comparing to.. Thus if the value passed in is null the comparison will always return true.
WHERE
[dbo].[Inscripciones].[Sede] = coalesce(#sede, [dbo].[Inscripciones].[Sede])
AND
[dbo].[Inscripciones].[Programa] = coalesce(#programa, [dbo].[Inscripciones].[Programa])
AND
[dbo].[Inscripciones].[Estado] = coalesce(#estado, [dbo].[Inscripciones].[Estado])
If sede is null and programa and estado are populated the compare would look like...
?=? (or 1=1)
?=programa variable passed in
?=Estado variable passed in
Boa Sorte!
Thank you all for your anwers. After reading the article posted in the comments by #SeanLange I was finally able to achieve what was needed. Using a CASE clause in the WHERE statement solves the deal. Here's the code:
SELECT
*
FROM [dbo].[Inscripciones]
WHERE
([dbo].[Inscripciones].[Sede] = (CASE WHEN #sede = '' THEN [dbo].[Inscripciones].[Sede] ELSE #sede END))
AND
([dbo].[Inscripciones].[Programa] = (CASE WHEN #programa = '' THEN [dbo].[Inscripciones].[Programa] ELSE #programa END))
AND
([dbo].[Inscripciones].[Estado] = (CASE WHEN #estado = '' THEN [dbo].[Inscripciones].[Estado] ELSE #estado END))
AND
([dbo].[Inscripciones].[TipoIngreso] = (CASE WHEN #tipoingreso = '' THEN [dbo].[Inscripciones].[TipoIngreso] ELSE #tipoingreso END))
Thanks again!!

How to write MDX Query for getting grouped results

I have Cube where data is aggregated and all I need to have count of records against each 2 digit zip code.
Attached image shows my cube hierarchies and measures.
I applied query like :
WITH MEMBER [Measures].NoOfConsignments as Count(([Consignment].[Target Address Name].[Target Address Name]))
select filter([Consignment].[Distribution Area].[Zip2], [Consignment].[Distribution Area].[Target Address Country] = "94") on rows,
{[Measures].NoOfConsignments} on columns
from [RebellOlap]
where ({[Consignment].[Distribution Area].[Target Address Country].&[94]})
but is throwing an error :
"The Distribution Area hierarchy already appears in the Axis1 axis"
I re-structured and formulated following sub-select query in the following way :
WITH MEMBER [Measures].NoOfConsignments as Count(([Consignment].[Target Address Name].[Target Address Name]))
Select
NON EMPTY [Measures].NoOfConsignments on columns,
NON EMPTY [Consignment].[Distribution Area].[Zip2] on rows
FROM (
SELECT {[Consignment].[Distribution Area].[Zip2],[Consignment].[Distribution Area].[Target Address Country].&[94]}
FROM [RebellOlap]
)
but it also returned me the 'ambiguity error'.
all I need Output in following manner
Edit
AllConsignments in Germany
All Consignments in Germany against specific Zip Code
I just applied filter for all zip codes and introduce "Range" as Operator with 'Filter Expression' and it worked!!
SELECT NON EMPTY { [Measures].[ConsignmentCount] } ON COLUMNS,
NON EMPTY { ([Consignment].[Distribution Area].[Zip2].ALLMEMBERS ) } ON ROWS
FROM ( SELECT ( [Consignment].[Distribution Area].[Zip2].&[94]&[0]&[01] : [Consignment].[Distribution Area].[Zip2].&[94]&[9]&[99] ) ON COLUMNS
FROM [RebellOlap])
Based on your edits, I feel below should work out for you:
WITH MEMBER Measures.NumConsignment as
COUNT
(
NonEmpty
(
[Consignment].[Target Address Name].[Target Address Name].MEMBERS,
([Consignment].[Distribution Area].CURRENTMEMBER, Measures.[Num. Consignments])
)
)
SELECT [Consignment].[Distribution Area].[Zip2].MEMBERS on 1,
Measures.NumConsignment ON 0
FROM
(
SELECT [Consignment].[Distribution Area].[Target Address Country].&[94] ON 0
FROM [RebellOlap]
)
While the rest of the code is pretty standard, there is one part which might need explanation.
NonEmpty
(
[Consignment].[Target Address Name].[Target Address Name].MEMBERS,
([Consignment].[Distribution Area].CURRENTMEMBER, Measures.[Num. Consignments])
)
This is returning a set of those Target addresses for the current zip for whom the num of consignments is not empty( i.e. <>0)

Splitting column with XML data

I have a SQL column named "details" and it contains the following data:
<changes><RoundID><new>8394</new></RoundID><RoundLeg><new>JAYS CLOSE AL6 Odds(1 - 5)</new></RoundLeg><SortType><new>1</new></SortType><SortOrder><new>230</new></SortOrder><StartDate><new>01/01/2009</new></StartDate><EndDate><new>01/01/2021</new></EndDate><RoundLegTypeID><new>1</new></RoundLegTypeID></changes>
<changes><RoundID><new>8404</new></RoundID><RoundLeg><new>HOLLY AREA AL6 (1 - 9)</new></RoundLeg><SortType><new>1</new></SortType><SortOrder><new>730</new></SortOrder><StartDate><new>01/01/2009</new></StartDate><EndDate><new>01/01/2021</new></EndDate><RoundLegTypeID><new>1</new></RoundLegTypeID></changes>
<changes><RoundID><new>8379</new></RoundID><RoundLeg><new>PRI PARK AL6 (1 - 42)</new></RoundLeg><SortType><new>1</new></SortType><SortOrder><new>300</new></SortOrder><StartDate><new>01/01/2009</new></StartDate><EndDate><new>01/01/2021</new></EndDate><RoundLegTypeID><new>1</new></RoundLegTypeID></changes>
What is the easiest way to separate this data out into individual columns? (that is all one column)
Try this:
SELECT DATA.query('/changes/RoundID/new/text()') AS RoundID
,DATA.query('/changes/RoundLeg/new/text()') AS RoundLeg
,DATA.query('/changes/SortType/new/text()') AS SortType
-- And so on and so forth
FROM (SELECT CONVERT(XML, Details) AS DATA
FROM YourTable) AS T
Once you get your result set from the sql (mysql or whatever) you will probably have an array of strings. As I understand your question, you wanted to know how to extract each of the xml nodes that were contained in the string that was stored in the column in question. You could loop through the results from the sql query and extract the data that you want. In php it would look like this:
// Set a counter variable for the first dimension of the array, this will
// number the result sets. So for each row in the table you will have a
// number identifier in the corresponding array.
$i = 0;
$output = array();
foreach($results as $result) {
$xml = simplexml_load_string($result);
// Here use simpleXML to extract the node data, just by using the names of the
// XML Nodes, and give it the same name in the array's second dimension.
$output[$i]['RoundID'] = $xml->RoundID->new;
$output[$i]['RoudLeg'] = $xml->RoundLeg->new;
// Simply create more array items here for each of the elements you want
$i++;
}
foreach ($output as $out) {
// Step through the created array do what you like with it.
echo $out['RoundID']."\n";
var_dump($out);
}

Resources