I have a data set as follows:
[
{
"Id": 1,
"Country": "Uruguay",
"Name": "Foo",
"Status": "Completed",
},
{
"Id": 2,
"Country": "Uruguay",
"Name": "Foo",
"Status": "Completed",
},
{
"Id": 3,
"Country": "Germany",
"Name": "Foo",
"Status": "Completed",
},
]
I want to transform and sort it by Country so that it looks as follows:
[
{
"Country": "Uruguay",
"Details": [
{
"Id": 1,
"Name": "Foo",
"Status": "Completed",
},
{
"Id": 2,
"Name": "Foo",
"Status": "Completed",
},
],
},
{
"Country": "Germany",
"Details": [
{
"Id": 3,
"Name": "Foo",
"Status": "Completed",
},
],
},
],
These are the classes in C#:
public class Countries {
public int Id { get; set; }
public string Country { get; set; }
public string Name { get; set; }
public string Status { get; set; }
}
public class Details {
public int Id { get; set; }
public string Name { get; set; }
public string Status { get; set; }
}
public class CountryList {
public string Country { get; set; }
public List<Details> Details { get; set; }
}
Some of what I have tried looks as followed:
var foo = countries
.GroupBy(x => new Details { Id = x.Id, Name = x.Name, Status = x.Status })
.Select( y => new CountryList
{
// Country = y.Key.
}
var foo = countries
.GroupBy(x => x.Country)
.Select( y => new CountryList
{
// Country = y.Key.
Details = y.GroupBy(a => new Details
{
Id = a.Id,
Name = a.Name,
Status = a.Status
}).ToList()
}
I am having trouble working out how to use LINQ to solve this. I have done a handful of GroupBy operations in the past, but I wasn't able to work this one out. How do I transform my dataset into the desired result?
You do not need second GroupBy
var foo = countries
.GroupBy(x => x.Country)
.Select(y => new CountryList
{
Country = y.Key,
Details = y.Select(a => new Details
{
Id = a.Id,
Name = a.Name,
Status = a.Status
}).ToList()
};
You can take advantage of the .GroupBy() overload that lets you define a resultSelector to create your CountryLists and populate their Details:
var countries = new List<Countries>
{
new() { Id = 1, Country = "Uruguay", Name = "Foo", Status = "Completed" },
new() { Id = 2, Country = "Uruguay", Name = "Foo", Status = "Completed" },
new() { Id = 3, Country = "Germany", Name = "Foo", Status = "Completed" },
};
List<CountryList> countryList = countries
.GroupBy(
c => c.Country,
( country, matches ) => new CountryList()
{
Country = country,
Details = matches.Select(match => new Details
{
Id = match.Id,
Name = match.Name,
Status = match.Status
}).ToList()
})
.ToList();
, ( country, matches ) => new CountryList() { ... } being the resultSelector.
Example fiddle here.
try this
var orig = JsonConvert.DeserializeObject<List<Countries>>(json);
List<CountryList> countries = orig.GroupBy(o => o.Country)
.Select(x => new CountryList {
Country = x.Key,
Details = x.Select(o => new Details {Id=o.Id,Name=o.Name,Status=o.Status} ).ToList()
}).ToList();
Related
Quick explanation
While fetching a nested collection from a JPQL query it serializes one parent object for each nested object. The intended behavior would be serializing one single parent object with a single collection/set of objects.
Using Spring DATA/DATA REST
JPQ Query
#Query(
"""
select distinct ticketOrder.id as id, user.name as name, ticket as tickets from TicketOrder ticketOrder
inner join User user on user.id = ticketOrder.user.id
inner join Ticket ticket on ticket.ticketOrder.id = ticketOrder.id
where user.uid = :uid
"""
)
fun findByUserUid(uid: UUID, pageable: Pageable): Page<TicketOrderProjection>
Related Entity Classes
TicketOrder
class TicketOrder(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "order_id", nullable = false)
val id: Long? = null,
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(nullable = false)
val user: User,
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(nullable = false, updatable = false)
val raffle: Raffle? = null,
#OneToMany(mappedBy = "ticketOrder", cascade = [CascadeType.ALL], orphanRemoval = true, fetch = FetchType.LAZY)
var tickets: MutableList<Ticket> = mutableListOf(),
#OneToOne(fetch = FetchType.LAZY)
var transaction: Transaction?,
#CreationTimestamp
#Column(name = "created_at", updatable = false)
val createdAt: Timestamp? = null,
#UpdateTimestamp
#Column(name = "updated_at")
val updatedAt: Timestamp? = null,
#Column(name = "completed_at", nullable = true)
var completedAt: LocalDateTime? = null,
#Column
var checkoutCode: String? = null,
#Enumerated(EnumType.STRING)
#Column(name = "order_status", nullable = false)
var status: TicketOrderStatus
)
/* ... omitted for brevity */
Ticket
#Entity
class Ticket(
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "ticket_id", nullable = false)
val id: Long? = null,
#Column(name = "ticket_number", nullable = false, columnDefinition = "UNIQUE")
val ticketNumber: Int,
#ManyToOne
#JoinColumn
var ticketOrder: TicketOrder? = null,
#ManyToOne
#JoinColumn
var raffle: Raffle
)
User
class User(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", nullable = false)
val id: Long? = null,
#Column(nullable = false)
val name: String,
#Column(nullable = false)
val phone: Long,
#Column(nullable = false)
val email: String,
#Column(nullable = false, updatable = false)
val uid: UUID = UUID.randomUUID(),
#CreationTimestamp
#Column(name = "created_at", updatable = false)
val createdAt: Timestamp? = null
)
Projections
TicketOrderProjection
#Projection(types = [TicketOrder::class])
interface TicketOrderProjection {
val id: Long
val name: String
val tickets: Set<TicketsProjection>
}
TicketsProjection
#Projection(types = [Ticket::class])
interface TicketsProjection {
#get:Value("#{target.id}")
val id: Int
}
Finally
Response at RestController
{
"content": [
{
"name": "Raphael Heizer",
"id": 10,
"tickets": [
{
"id": 21
}
]
},
{
"name": "Raphael Heizer",
"id": 10,
"tickets": [
{
"id": 22
}
]
},
{
"name": "Raphael Heizer",
"id": 10,
"tickets": [
{
"id": 23
}
]
}
],
"pageable": {
// omitted
}
Expected response
{
"content": [
{
"name": "Raphael Heizer",
"id": 10,
"tickets": [
{
"id": 21,
},
{
"id": 22
},
{
"id": 23
}
]
},
],
"pageable": {
// omitted
}
In my database I have 3 main classes which are Breeders, Horses and Races. These 3 tables have many-to-many relationships. A breeder can have multiple horses and a horse can have multiple breeders. Also a horse can enter multiple races and a race can have multiple horses entered. Now I want to query a breeders race stats. Using entity framework I have this line of code:
var races = DbContext.HorseBreeders
.Where(w => w.BreederId == someint)
.SelectMany(s => s.Horse.Races)
.ToList();
and this code generates this mysql query:
SELECT `w.Horse.Races`.`Id`, `w.Horse.Races`.`FinishTime`, `w.Horse.Races`.`Horse_Id`, `w.Horse.Races`.`Race_Id`
FROM `HorseBreeders` AS `w`
INNER JOIN `Horses` AS `w.Horse` ON `w`.`HorseId` = `w.Horse`.`Id`
INNER JOIN `RaceEntries` AS `w.Horse.Races` ON `w.Horse`.`Id` = `w.Horse.Races`.`Horse_Id`
WHERE `w`.`BreederId` = someint
and this query takes around 30 seconds.
Breeders table has 13k records
Horses table has 60k records
HorseBreeders table has 40k records
Races table has 110k records
RaceEntries table has 960k records.
In Mssql this same structure with a similar query was taking less than a second. But in Mysql its taking too much time. What am I doing wrong?
DbContext Classes:
public class Breeder
{
public int Id { get; set; }
[StringLength(50)]
public string Name { get; set; }
public virtual ICollection<HorseBreeder> Horses { get; set; }
}
public class Horse
{
public int Id { get; set; }
[StringLength(50)]
public string Name { get; set; }
public int? Father_Id { get; set; }
public virtual Horse Father { get; set; }
public int? Mother_Id { get; set; }
public virtual Horse Mother { get; set; }
public string BirthPlace { get; set; }
public DateTime? BirthDate { get; set; }
public virtual ICollection<HorseBreeder> Breeders { get; set; }
public virtual ICollection<RaceEntry> Races { get; set; }
public virtual ICollection<Horse> FatherChilds { get; set; }
public virtual ICollection<Horse> MotherChilds { get; set; }
}
public class HorseBreeder
{
public int HorseId { get; set; }
public Horse Horse { get; set; }
public int BreederId { get; set; }
public Breeder Breeder { get; set; }
}
public class Race
{
public int Id { get; set; }
public DateTime Time { get; set; }
public string Name { get; set; }
public virtual ICollection<RaceEntry> Horses { get; set; }
}
public class RaceEntry
{
public int Id { get; set; }
public int Race_Id { get; set; }
public virtual Race Race { get; set; }
public int Horse_Id { get; set; }
public virtual Horse Horse { get; set; }
public short? FinishTime { get; set; }
}
DbContext OnModelCreating:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Horse
builder.Entity<HorseBreeder>()
.HasKey(bc => new { bc.HorseId, bc.BreederId });
builder.Entity<HorseBreeder>()
.HasOne(bc => bc.Horse)
.WithMany(b => b.Breeders)
.HasForeignKey(bc => bc.HorseId);
builder.Entity<HorseBreeder>()
.HasOne(bc => bc.Breeder)
.WithMany(c => c.Horses)
.HasForeignKey(bc => bc.BreederId);
builder.Entity<Horse>()
.HasOne(p => p.Father)
.WithMany(p => p.FatherChilds)
.HasForeignKey(p => p.Father_Id);
builder.Entity<Horse>()
.HasOne(p => p.Mother)
.WithMany(p => p.MotherChilds)
.HasForeignKey(p => p.Mother_Id);
// RaceEntry
builder.Entity<RaceEntry>()
.HasOne(m => m.Race)
.WithMany(t => t.Horses)
.HasForeignKey(m => m.Race_Id);
builder.Entity<RaceEntry>()
.HasOne(m => m.Horse)
.WithMany(t => t.Races)
.HasForeignKey(m => m.Horse_Id);
}
Database create migration:
public partial class initial_create : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Breeders",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Name = table.Column<string>(maxLength: 50, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Breeders", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Horses",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Name = table.Column<string>(maxLength: 50, nullable: true),
Father_Id = table.Column<int>(nullable: true),
Mother_Id = table.Column<int>(nullable: true),
BirthPlace = table.Column<string>(nullable: true),
BirthDate = table.Column<DateTime>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Horses", x => x.Id);
table.ForeignKey(
name: "FK_Horses_Horses_Father_Id",
column: x => x.Father_Id,
principalTable: "Horses",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Horses_Horses_Mother_Id",
column: x => x.Mother_Id,
principalTable: "Horses",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "Races",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Time = table.Column<DateTime>(nullable: false),
Name = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Races", x => x.Id);
});
migrationBuilder.CreateTable(
name: "HorseBreeders",
columns: table => new
{
HorseId = table.Column<int>(nullable: false),
BreederId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_HorseBreeders", x => new { x.HorseId, x.BreederId });
table.ForeignKey(
name: "FK_HorseBreeders_Breeders_BreederId",
column: x => x.BreederId,
principalTable: "Breeders",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_HorseBreeders_Horses_HorseId",
column: x => x.HorseId,
principalTable: "Horses",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "RaceEntries",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Race_Id = table.Column<int>(nullable: false),
Horse_Id = table.Column<int>(nullable: false),
FinishTime = table.Column<short>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_RaceEntries", x => x.Id);
table.ForeignKey(
name: "FK_RaceEntries_Horses_Horse_Id",
column: x => x.Horse_Id,
principalTable: "Horses",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_RaceEntries_Races_Race_Id",
column: x => x.Race_Id,
principalTable: "Races",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_HorseBreeders_BreederId",
table: "HorseBreeders",
column: "BreederId");
migrationBuilder.CreateIndex(
name: "IX_Horses_Father_Id",
table: "Horses",
column: "Father_Id");
migrationBuilder.CreateIndex(
name: "IX_Horses_Mother_Id",
table: "Horses",
column: "Mother_Id");
migrationBuilder.CreateIndex(
name: "IX_RaceEntries_Horse_Id",
table: "RaceEntries",
column: "Horse_Id");
migrationBuilder.CreateIndex(
name: "IX_RaceEntries_Race_Id",
table: "RaceEntries",
column: "Race_Id");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "HorseBreeders");
migrationBuilder.DropTable(
name: "RaceEntries");
migrationBuilder.DropTable(
name: "Breeders");
migrationBuilder.DropTable(
name: "Horses");
migrationBuilder.DropTable(
name: "Races");
}
}
I have three different tables.
1. Order details (id, itemId,date,userId)
2. Item details (id, itemName, quantity)
3. User details (id, userName)
I want to return a JSON as:
{[
{
orderId = 1,
items = [
{
itemId = 1,
itemName = ITEM_DEMO,
},
{
itemId = 2,
itemName = ITEM_DEMO2,
}
],
userDetails = {
userId = 1,
userName = TEST_USER
}
}
]}
How can we do this in Node JS. Im using MySQL.
First of all you should read about object and json in javascript.
For parsing your data from mySql to json do the following steps:
Create an object that is filled with your data from mySql.
const ArrayObjectFilledWithMysqlData = [
{
orderId: 1,
items: [{
itemId: 1,
itemName: ITEM_DEMO,
},
{
itemId: 2,
itemName: ITEM_DEMO2,
}]
},
{
userDetails: {
userId: 1,
userName: TEST_USER
}
}
]
Parse the Array to json.
const jsonFromData = JSON.stringify(ArrayObjectFilledWithMysqlData)
I am implementing auto-compete textbox using MVC 4. According to the requirement the textbox can take multiple names(here EmployeeName) separated by "," (comma). I have written the following code for the controller:
public JsonResult EmployeeList(string strEmpName)
{
List<string> list = strEmpName.Split(',').ToList();
list = list.Select(s => s.Trim()).ToList();
//Extract the term to be searched from the list
string searchTerm = list.LastOrDefault().ToString().Trim();
//Return if Search Term is empty
if (string.IsNullOrEmpty(searchTerm))
{
//return new string[0];
}
List<string> excludeEmployeeName = new List<string>();
if (list.Count > 1)
{
list.RemoveAt(list.Count - 1);
excludeEmployeeName = list;
}
var query = (EmployeeDAL.GetEmployee().Where(x => x.EmployeeName.StartsWith(searchTerm.ToString().ToUpper()))).ToList();
var myList = new List<string>();
foreach (var emp in query.ToList())
{
myList.Add(emp.EmployeeName.ToString());
}
return Json(myList, JsonRequestBehavior.AllowGet);
}
The above function is returning JSON.
Data is like:
public class EmployeeDAL
{
public static List<Employee> GetEmployee()
{
var empList = new List<Employee>
{
new Employee { EmployeeId = "E001", EmployeeName = "Alex", DepartmentId = 1 },
new Employee { EmployeeId = "E002", EmployeeName = "Alan", DepartmentId = 2 },
new Employee { EmployeeId = "E003", EmployeeName = "Johny", DepartmentId = 3 },
new Employee { EmployeeId = "E004", EmployeeName = "Joe", DepartmentId = 1 },
new Employee { EmployeeId = "E005", EmployeeName = "Thomas", DepartmentId = 3 },
new Employee { EmployeeId = "E006", EmployeeName = "Sarah", DepartmentId = 3 },
new Employee { EmployeeId = "E007", EmployeeName = "Rahul", DepartmentId = 4 },
new Employee { EmployeeId = "E008", EmployeeName = "Raman", DepartmentId = 1 },
new Employee { EmployeeId = "E009", EmployeeName = "Mandy", DepartmentId = 2 },
new Employee { EmployeeId = "E010", EmployeeName = "Rohit", DepartmentId = 4 },
new Employee { EmployeeId = "E011", EmployeeName = "Sanjay", DepartmentId = 1 },
new Employee { EmployeeId = "E012", EmployeeName = "Abhi", DepartmentId = 3 },
new Employee { EmployeeId = "E013", EmployeeName = "Martin", DepartmentId = 1 },
new Employee { EmployeeId = "E014", EmployeeName = "Robin", DepartmentId = 2 },
new Employee { EmployeeId = "E015", EmployeeName = "Rohit", DepartmentId = 1 },
new Employee { EmployeeId = "E016", EmployeeName = "William", DepartmentId = 3 },
new Employee { EmployeeId = "E017", EmployeeName = "Sourav", DepartmentId = 2 },
new Employee { EmployeeId = "E018", EmployeeName = "Alex", DepartmentId = 2 },
};
return empList.ToList();
}
}
In view I have written the following script:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.1/jquery-ui.min.js"></script>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" rel="Stylesheet" type="text/css" />
<script type="text/javascript">
$(document).ready(function() {
SearchText();
});
function SearchText() {
$("#txtEmployeeName").autocomplete({
source: function(request, response) {
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "Employee/EmployeeList",
data: "{'strEmpName':'" + extractLast(request.term) + "'}",
dataType: "json",
success: function(data) {
response(data.d);
},
error: function(result) {
alert(result.responseText);
}
});
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function(event, ui) {
var terms = split(this.value);
// remove the current input
terms.pop();
// add the selected item
terms.push(ui.item.value);
// add placeholder to get the comma-and-space at the end
terms.push("");
this.value = terms.join(", ");
return false;
}
});
$("#txtEmployeeName").bind("keydown", function (event) {
if (event.keyCode === $.ui.keyCode.TAB &&
$(this).data("autocomplete").menu.active) {
event.preventDefault();
}
})
function split(val) {
return val.split(/,\s*/);
}
function extractLast(term) {
return split(term).pop();
}
}
</script>
When I run the application and debug the json script using firebug, I can see the data is coming rightly according the letter I type in the text box (txtEmployeeName) but is not showing in the browser window. I have given the total script so that any one can run it.
Any help will be thankfully accepted.
3 tables in MYSQL
table_product - product_id, product_name
table_variane - variant_id, variant_name
table_product_variants - product_id, variant_id, MRP, SellPrice
I want to create JSON data out of those in Perl for all the products, in this format:
[
{
"ProductID": "1",
"ProductName": "Green Detergent Bar",
"Variants": [
{
"VariantID": "1",
"VariantName": "500GM",
"MRP": "20.00",
"SellPrice": "19.50"
},
{
"VariantID": "2",
"VariantName": "1KG",
"MRP": "40.00",
"SellPrice": "38.00"
}
]
},
{
"ProductID": "2",
"ProductName": "ABCD",
"Variants": [
{
"VariantID": "3",
"VariantName": "1KG",
"MRP": "200.00",
"SellPrice": "190.50"
},
{
"VariantID": "2",
"VariantName": "1KG",
"MRP": "40.00",
"SellPrice": "38.00"
}
]
}
]
This is Perl Code
my $sql_query = ""; //need to fill this.
my $statement = $db_handle->prepare ($sql_query) or die "Couldn't prepare query '$sql_query': $DBI::errstr\n";
$statement->execute() or die "SQL Error: $DBI::errstr\n";
my #loop_data = ();
while (my #data = $statement->fetchrow_array())
{
my %data = //need to fill this too.
push(#loop_data, \%data);
}
my $json_text = to_json(\#loop_data);
print $json_text;
Please help in filling SQL query and while loop.
Its just a blueprint. Any modification in code is also fine.
That you need arrays (not hashes) complicates things a bit.
Option 1
Use two queries, one that finds the products, and one that finds the variants of a product.
my $product_sth = $dbh->prepare("
SELECT product_id,
product_name
FROM table_product
");
my $variant_sth = $dbh->prepare("
SELECT tv.variant_id,
tv.variant_name,
tvp.MRP,
tvp.SellPrice
FROM table_product_variants AS tpv
JOIN table_variant AS tv
ON tpv.variant_id = tv.variant_id
WHERE tpv.product_id = ?
");
my #data;
while (my $product_row = $product_sth->fetchrow_hashref()) {
my #variants;
$variant_sth->execute($product_row->{product_id});
while (my $variant_row = $variant_sth->fetchrow_hashref()) {
push #variants, {
VariantID => $variant_row->{variant_id},
VariantName => $variant_row->{variant_name},
MRP => $variant_row->{MRP},
SellPrice => $variant_row->{SellPrice},
};
}
push #data, {
ProductID => $product_row->{product_id},
ProductName => $product_row->{product_name},
Variants => \#variants,
};
}
my $data_json = to_json(\#data);
Option 2
Use an HoA to group the variants of a product when using a single query.
my $sth = $dbh->prepare("
SELECT tp.product_id,
tp.product_name,
tv.variant_id,
tv.variant_name,
tvp.MRP,
tvp.SellPrice
FROM table_product AS tp
JOIN table_product_variants AS tpv
ON tp.product_id = tpv.product_id
JOIN table_variant AS tv
ON tpv.variant_id = tv.variant_id
");
my %data;
while (my $row = $sth->fetchrow_hashref()) {
my $product_id = $row->{product_id};
my $product = $data{$product_id} ||= {
ProductID => $row->{product_id},
ProductName => $row->{product_name},
Variants => [],
};
push #{ $product->{Variants} }, {
VariantID => $row->{variant_id},
VariantName => $row->{variant_name},
MRP => $row->{MRP},
SellPrice => $row->{SellPrice},
};
}
my $data_json = to_json([ values(%data) ]);
Option 3
Use sorting to group the variants of a product when using a single query.
my $sth = $dbh->prepare("
SELECT tp.product_id,
tp.product_name,
tv.variant_id,
tv.variant_name,
tvp.MRP,
tvp.SellPrice
FROM table_product AS tp
JOIN table_product_variants AS tpv
ON tp.product_id = tpv.product_id
JOIN table_variant AS tv
ON tpv.variant_id = tv.variant_id
ORDER BY tp.product_id
");
my $last_product_id = 0;
my #data;
while (my $row = $sth->fetchrow_hashref()) {
my $product_id = $row->{product_id};
if ($product_id != $last_product_id) {
$last_product_id = $product_id;
push #data, {
ProductID => $row->{product_id},
ProductName => $row->{product_name},
Variants => [],
};
}
push #{ $data[-1]{Variants} }, {
VariantID => $row->{variant_id},
VariantName => $row->{variant_name},
MRP => $row->{MRP},
SellPrice => $row->{SellPrice},
};
}
my $data_json = to_json(\#data);
It's a little extra work than option 2, but it has the smallest client-side memory footprint (if you didn't have to keep everything in memory).