SQL cross-table calculation in ASP.net - asp.net

I'm hoping someone can help with a problem I can't wrap my limited knowledge around. I have two SQL queries like:
SELECT
SUM(Matrix.[ITEM1]) AS [ITEM1],
SUM(Matrix.[ITEM2]) AS [ITEM2]
FROM StoreList
INNER JOIN Matrix ON StoreList.[Store Number] = Matrix.[Store Number]
WHERE (StoreList.Region = '#Value')
SELECT
ITEMTYPES.type_description,
ITEMS.Quantity
FROM ITEMS
INNER JOIN ITEMTYPES ON ITEMS.type_id = ITEMTYPES.id
Now, what these do is return a list of values that I want to multiply together (for example, to get a final quantity for [ITEM1], multiply [ITEM1] by [Items.Quantity]. However the ITEMTYPES table holds a list of descriptions of ITEM (and primary key id), which is used for entering new items. The STORELIST table uses the descriptors in ITEMTYPES as column names(ie. The ItemTypes table has records called [Item1], [Item2]; the Column names in Matrix are also [Item1], [Item2]).
The question is this - from my limited experience (and I do mean limited - < 6months with SQL) there is no way to perform a calculation based upon column names - I would have to abstract out the totals in some way to another table, with additional fields for Region (this explains the sum() statements in query 1) and perform calculations that way - or does some have a more elegant idea?
Table Structures as per Laurence's request:
For Table [MATRIX]:
[Store Number] (PK, int, not null)
[ITEM1] (int, default 0)
[ITEM2] (int, default 0)
For Table [ITEMTYPES]:
[id] (PK, int, not null),
[ITEMTYPE] (nvarchar(50), not null)
Now, Example data (excuse any formatting issues!):
[MATRIX]
Store Number ITEM1 ITEM2
------------------------------
123456 1 15
678920 31 9
[ITEMTYPE]
id ITEMTYPE
------------------------------
1 ITEM1
2 ITEM2
Appreciate the response!

Ok so you have 2 results returned from these 2 queries into 2 different ordered pair lists or arrays... so you have the first result in the format of
//FROM THE MATRIX QUERY
array(
[0] => array(
"Store Number" => 123456,
"ITEM1" => 1,
"ITEM2" => 15
),
[1] => array(
"Store Number" => 678920,
"ITEM1" => 31,
"ITEM2" => 9
),
ETC...
)
//FROM YOUR ITEM QUERY
array(
[0] => array(
"type_description" => "ITEM1",
"quantity" => ???
),
[1] => array(
"type_description" => "ITEM2",
"quantity" => ???
)
ETC......
)
ok now you can do 2 foreach loops where you iterate through the 2 arrays
foreach(arrayMatrix as store){
foreach(arrayItems as item){
print "Store: "+ store['Store Number']+" ITEM: " + item['type_description']+" total: "+ (store[item['type_description']] * item['quantity']);
}
}
now I wrote this in psuedo code and Im not sure if you can translate this well into VB. With those 2 foreach or while loops or whatever you can databind the results to the grid or put them in another array or list... I hope this helps at all.

Related

Kusto KQL: how to check if JSON array in dataset contains element of another array?

The dataset (table) I'm querying has a column containing a JSON string array.
I have a fixed list of verbs which I need to check against each entry in the table and find those, where at least one of the items in the JSON list starts with one of the verbs from the fixed list.
// Verbs to look for (actual list is longer).
let verbs = datatable (verb : string) [
"discover",
"gain"
];
// Data. Second column is a JSON string.
let data = datatable(id : int, json: string) [
1, "[\"Discover me\", \"some text\"]",
2, "[\"All good\", \"no invalid verbs\"]",
3, "[\"first element fine\", \"gain power isn't ok\"]",
];
// Query: I need to know if at least one of the items in the "json" column starts
// with one of the verbs of the "verbs" list.
data
| extend parsedJson = parse_json(json)
| extend OneOrMoreListItemsHaveVerb = false
| project id, OneOrMoreListItemsHaveVerb
I tried to use mv_apply() but failed because I'm dealing with two lists/arrays compared against each other, not one array and one item.
For the example data above, I expect items with IDs 1 and 3 to be returned. The first element of item 1 has "discover" and the 2nd element of item 3 starts with "gain".
you could create an array from your input table (e.g. using summarize make_set()), then loop over it using mv-apply foreach of the inputs.
for example:
let verbs = datatable (verb: string) [
"discover", "gain"
]
;
let verbs_list = toscalar(verbs | summarize make_set(verb))
;
let data = datatable(id: int, json: string) [
1, "[\"Discover me\", \"some text\"]",
2, "[\"All good\", \"no invalid verbs\"]",
3, "[\"first element fine\", \"gain power isn't ok\"]",
]
;
data
| mv-apply verb = verbs_list on (
mv-apply input = parse_json(json) on (
where input startswith verb
)
)
| project ['id'], json
id
json
1
["Discover me", "some text"]
3
["first element fine", "gain power isn't ok"]
alternatively, you can implement similar logic using the partition operator:
let verbs = datatable (verb: string) [
"discover", "gain"
]
;
let data = datatable(id: int, json: string) [
1, "[\"Discover me\", \"some text\"]",
2, "[\"All good\", \"no invalid verbs\"]",
3, "[\"first element fine\", \"gain power isn't ok\"]",
]
;
verbs
| partition by verb
{
data
| mv-apply input = parse_json(json) on(
where input startswith verb
)
| project ['id'], json
}
id
json
1
["Discover me", "some text"]
3
["first element fine", "gain power isn't ok"]

Count results for group by query

I have a complex query which group bys multiple columns, this is an example
$queryBuilder
->join('e.something', 's')
->where('...')
->addGroupBy('e.id s.someField');
Using ->getQuery()->getResults() returns me an array of entities like I'd expect.
[
0 => App\Entity\ExampleEntity,
1 => App\Entity\ExampleEntity
]
If I try to count the results returned using a select like so:
$queryBuilder
->select('COUNT(e.id)')
->join('e.something', 's')
->where('...')
->addGroupBy('e.id s.someField');
I am returned an array of arrays, inside each array is the count. This isn't what I want. Removing the group by I'm given the correct result however the group by is required.
[
[
1 => '11',
],
[
1 => '4',
]
]
I'm stuck on how I can count the results of the group by. I have tried using distinct and I also do not want to count in PHP.

Doctrine inner query set parameters

I'm trying to execute an inner query and set parameters without success, i always get:
Invalid parameter number: number of bound variables does not match number of tokens
This is my query :
SELECT i2
FROM MyBundle:Identity i2
INNER JOIN MyBundle:Property\\Mapped\\Email propertyEmail WITH propertyEmail.identity = i2
INNER JOIN MyBundle:Channel channel WITH propertyEmail.channel = channel
WHERE (
SELECT COUNT(mappedEmail)
FROM MyBundle:Property\\Mapped\\Email mappedEmail
WHERE mappedEmail.identity = i2
AND mappedEmail.channel = :channel
AND mappedEmail.createdAt <= :dateFrom
) > 0
AND IDENTITY(propertyEmail.channel) <> :channel
AND propertyEmail.createdAt <= :dateFrom
GROUP BY i2
And this is how i put as subquery:
$innerQuery = $queryBuilder->getEntityManager()->createQuery($query)
->setParameters([
'channel' => $channelId,
'dateFrom' => $dateFrom
])
;
$queryBuilder->andWhere($queryBuilder->expr()->notIn($alias, $innerQuery->getDQL()));
That return the error :
Invalid parameter number: number of bound variables does not match number of tokens
If I execute the query as single query all works right.
Thanks for suggestion.
Try to apply the parameters to the main query as follow:
$innerQuery = $queryBuilder->getEntityManager()->createQuery($query);
$queryBuilder
->andWhere($queryBuilder->expr()->notIn($alias, $innerQuery->getDQL()))
->setParameters([
'channel' => $channelId,
'dateFrom' => $dateFrom
]);

Two foreign keys reference to same table in symfony2 querybuilder

Table1(identity1, a1,b1)
Table2(identity, foreign(identity1),foreign(identity1))
Now when I use this query
$query = $qb->select('a1', ' b1')
->from('table2', 'm1')
->join('ApiMapBundle:tabl1, 'u1', 'WITH', $qb->expr()->orX('m1. foreign(a1) =u1.identity', 'm1. foreign(b1) = u1.identity '))
->andWhere('m1.identity=:tt')
->setParameter('tt', $cn)
->getQuery()
->getResult();
Now the problem with this query is that sometimes it gives id let's tt:5 so it gives me a value like this
Array(
0 => Array(id => 1, b1=> 8000225),
1 => Array(id => 9, b1 => 8000234)) given).
Basically in table the values structure is like this
Table2(Identity=5,foreign1=9,foreign2=1)
Any idea that how can I exactly get the given structure? Because in some cases it is fine they give me proper foreign 1 and foreign 2 but in other cases it make it alternative. Any idea?

Consolidating values from multiple tables

I have an application which has data spread accross 2 tables.
There is a main table Main which has columns - Id , Name, Type.
Now there is a Sub Main table that has columns - MainId(FK), StartDate,Enddate,city
and this is a 1 to many relation (each main can have multiple entries in submain).
Now I want to display columns Main.Id, City( as comma seperated from various rows for that main item from submain), min of start date(from submain for that main item) and max of enddate( from sub main).
I thought of having a function but that will slow things up since there will be 100k records. Is there some other way of doing this. btw the application is in asp.net. Can we have a sql query or some linq kind of thing ?
This is off the top of my head, but firstly I would suggest you create a user defined function in sql to create the city comma separated list string that accepts #mainid, then does the following:
DECLARE #listStr VARCHAR(MAX)
SELECT #listStr = COALESCE(#listStr+',' , '') + city
FROM submain
WHERE mainid = #mainid
... and then return #listStr which will now be a comma separated list of cities. Let's say you call your function MainIDCityStringGet()
Then for your final result you can simply execute the following
select cts.mainid,
cts.cities,
sts.minstartdate,
sts.maxenddate
from ( select distinct mainid,
dbo.MainIDCityStringGet(mainid) as 'cities'
from submain) as cts
join
( select mainid,
min(startdate) as 'minstartdate',
max(enddate) as 'maxenddate'
from submain
group by mainid ) as sts on sts.mainid = cts.mainid
where startdate <is what you want it to be>
and enddate <is what you want it to be>
Depending on how exactly you would like to filter by startdate and enddate you may need to put the where filter within each subquery and in the second subquery in the join you may then need to use the HAVING grouped filter. You did not clearly state the nature of your filter.
I hope that helps.
This will of course be in stored procedure. May need some debugging.
An alternative to creating a stored procedure is performing the complex operations on the client side. (untested):
var result = (from main in context.Main
join sub in context.SubMain on main.Id equals sub.MainId into subs
let StartDate = subs.Min(s => s.StartDate)
let EndDate = subs.Max(s => s.EndDate)
let Cities = subs.Select(s => s.City).Distinct()
select new { main.Id, main.Name, main.Type, StartDate, EndDate, Cities })
.ToList()
.Select(x => new
{
x.Id,
x.Name,
x.Type,
x.StartDate,
x.EndDate,
Cities = string.Join(", ", x.Cities.ToArray())
})
.ToList();
I am unsure how well this is supported in other implimentations of SQL, but if you have SQL Server this works a charm for this type of scenario.
As a disclaimer I would like to add that I am not the originator of this technique. But I immediately thought of this question when I came across it.
Example:
For a table
Item ID Item Value Item Text
----------- ----------------- ---------------
1 2 A
1 2 B
1 6 C
2 2 D
2 4 A
3 7 B
3 1 D
If you want the following output, with the strings concatenated and the value summed.
Item ID Item Value Item Text
----------- ----------------- ---------------
1 10 A, B, C
2 6 D, A
3 8 B, D
The following avoids a multi-statement looping solution:
if object_id('Items') is not null
drop table Items
go
create table Items
( ItemId int identity(1,1),
ItemNo int not null,
ItemValue int not null,
ItemDesc nvarchar(500) )
insert Items
( ItemNo,
ItemValue,
ItemDesc )
values ( 1, 2, 'A'),
( 1, 2, 'B'),
( 1, 6, 'C'),
( 2, 2, 'D'),
( 2, 4, 'A'),
( 3, 7, 'B'),
( 3, 1, 'D')
select it1.ItemNo,
sum(it1.ItemValue) as ItemValues,
stuff((select ', ' + it2.ItemDesc --// Stuff is just used to remove the first 2 characters, instead of a substring.
from Items it2 with (nolock)
where it1.ItemNo = it2.ItemNo
for xml path(''), type).value('.','varchar(max)'), 1, 2, '') as ItemDescs --// Does the actual concatenation..
from Items it1 with (nolock)
group by it1.ItemNo
So you see all you need is a sub query in your select that retrieves a set of all the values you need to concatenate and then use the FOR XML PATH command in that sub query in a clever way. It does not matter where the values you need to concatenate comes from you just need to retrieve them using the sub query.

Resources