2025-09-06 18:42:13 EDT

M: I'm using the mobile database program Ninox. I think that's how it's pronounced. N-I-N-O-X. Can you tell me if there's an easy way when inputting to make sure that whatever is put in is not a duplicate entry when it's the, well I don't know if it's a the question, but when it is containing duplicate information. Like on a karaoke database, I don't want to end up putting in a song twice if it already exists.

G: Yep, you’ve got the name right—it’s Ninox 👍. And yes, you can absolutely set it up so you don’t accidentally add duplicates, though how you do it depends a little on whether you want Ninox to block the duplicate or just warn you.

Option 1: Use a Unique Field

Option 2: Use a Trigger on Create

let dupe := (select Songs where SongTitle = this.SongTitle and Artist = this.Artist);
if count(dupe) > 1 then
    alert("This song already exists in the database!");
    delete this
end

That way, if you add the same song twice, Ninox pops up a message and removes the duplicate.

Option 3: Use a Validation Rule


✨ For a karaoke list, the combined field marked unique approach (Option 1) is usually the easiest—it’s quick, clean, and doesn’t require scripting unless you want more control.

Do you want me to sketch out how to set up that combined Song-Artist unique field step by step, so you don’t even have to touch scripting?

2025-09-06 18:43:41 EDT

M: What if I'm using a CSV import? Would I be able to do anything then that would prevent duplicates in case there already existed a record with the same information or if somehow I ended up with two copies of the same entry in the CSV itself?

G: Ah, good catch — imports are trickier because they can flood your table in one go 🚰. Ninox won’t automatically de-dupe during CSV import, but you can set things up so duplicates either get blocked or are easy to purge afterward.

Before Import

During Import

After Import

let dups := select Songs group by SongTitle, Artist having count(*) > 1;
alert("Found " + text(count(dups)) + " duplicates.");

From there, you could expand it to list or delete the extras.


💡 Practical tip:
If you expect to import CSVs regularly, it’s usually safest to:

  1. Add a unique combined field (SongTitle + " - " + Artist).
  2. Run the import — duplicates won’t overwrite, they’ll just fail quietly.
  3. Check the report Ninox gives you after import to see if anything was skipped.

That way, you can confidently load huge CSVs without manually scanning for doubles.

Would you like me to show you exactly how to set up that combined unique field so CSV imports automatically respect it?

2025-09-06 19:11:51 EDT

M: I'm not seeing any option to choose a unique field. Do you have any idea where that would be? I'm not using the paid version. Is it something to do with that?

G: You're absolutely right to notice that there’s no setting labeled “Unique Field” in Ninox—even in the paid version. That kind of built-in unique constraint simply doesn’t exist yet. It’s not a permissions issue or tied to your version. It’s a limitation of the platform itself. citeturn0search1


What others have said:

From the Ninox community forum:

“There is no ‘easy’ way to mark a field as ‘unique’ … The only way is to create a script that ensures there is no duplicate.” citeturn0search1

So, sadly, even though SQLite (which Ninox uses under the hood) supports unique constraints, Ninox has not exposed that functionality in its UI.


What you can do instead:

Since there's no native unique field, you'll want to build a workaround using scripting. Here are the most common approaches:

1. Trigger After Update / After Create

Use a script on the field or record that checks for duplicates and alerts the user.

For example, if your fields are Song Title and Artist:

let title := this.SongTitle;
let artist := this.Artist;
if count(select Songs where SongTitle = title and Artist = artist) > 1 then
  alert("This song (and artist) already exists!");
  // Optionally clear a field or delete this record:
  this.SongTitle := null;
end

This can run after the record is created or after a specific field is updated to immediately catch duplicates. citeturn0search4turn0search15

2. Formula Field to Flag Duplicates

You can add a Read-Only Formula field that marks records as “Duplicate” or “Unique”:

let t := this;
if count(select Songs where SongTitle = t.SongTitle and Artist = t.Artist) > 1 then
  "Duplicate"
else
  "Unique"
end

This way, duplicates are flagged visually and can be filtered or sorted out. citeturn0search5

3. CSV Import Scripted Handling

If you're using CSV imports, you can write a custom import routine (via script) that:

Here's an example pattern (adapted for your karaoke songs):

for row in csvRows do
   let existing := first(select Songs where SongTitle = row.Title and Artist = row.Artist);
   if existing then
     // Optionally update fields
     existing.Album := row.Album;
     // ... other updates
   else
     let newRec := create Songs;
     newRec.SongTitle := row.Title;
     newRec.Artist := row.Artist;
     newRec.Album := row.Album;
   end
end

This is similar to a real-world example shared for preventing duplicate products during import. citeturn0search3

4. Dashboard + Server-Side Script (Advanced)

If you're on the web version, there's a sample CSV import tool that uses a dashboard and server-side scripting to check duplicates across tables while importing. It’s more elaborate, but powerful if you want to streamline regular imports. citeturn0search10


Quick Summary Table

Problem / Goal Solution Approach
Prevent duplicates on manual entry Use Trigger scripts (After Create / After Update)
Flag duplicates for review Use a Formula field to label “Duplicate” or “Unique”
Avoid duplicate records from imports Scripted import: select → update or create logic
Automate import with dedupe logic Use dashboard + server-side script (web-only)

Final Thoughts

Would you like a ready-made Ninox script example tailored exactly to your Song-Artist karaoke setup?

2025-09-06 19:41:18 EDT

M: Here's something more advanced. What if I'm trying to create something a little bit more complex? I want a field... No, scratch that. I think I was using the wrong words. I want to create a record which can contain a variable number of predefined combinations of various qualities. Okay, let's just stop being vague. I'm trying to create a database based on the Marvel Puzzle Quest phone game, just to see if I can do it. And with a little bit of utility, because in some of the actual puzzle stages, you have to perform actions like, I don't know, create blue tiles. So, what I'm trying to do is create a record that contains a variable list of effects that each power can have. So, some powers can transform any tile into a blue one, while others will randomly transform only basic tiles into blue tiles. And a third one might transform any attack tiles that are selected into a blue tile. And so, I'd like to be able to include a variable number of those kinds of descriptors into the definition record. Is that even possible?

G: [Non-text content: thoughts; content_type=thoughts]

G: [Non-text content: reasoning_recap; content_type=reasoning_recap]

G: TL;DR: Yes. Model each Power as a record with a child table of Effects. Each Effect is one “descriptor tuple” (e.g., Transform → Blue → Random → Only Basic Tiles). Because Effects are separate records linked to the Power, you can attach any number of them. Use small lookup tables (or Choice fields) for the parts of the tuple, and add a tiny script to prevent duplicate tuples on the same Power.

How to model it in Ninox

1) Tables (minimal, flexible)

Tip: If you want a very stable vocabulary, make lookup tables (e.g., EffectAction, TileClass) and reference them. If you prefer simple and quick, use Choice fields.

2) Show variable Effects on a Power

In the Powers form, add a subtable view of Effects. That lets you add as many Effect rows as you need for that Power.

3) Make tuple duplicates impossible (per Power)

Add a Formula field on Effects named EffectKey:

// Effects.EffectKey (Formula)
upper(text(Action)) 
+ "|" + upper(text(Color)) 
+ "|" + upper(text(Selection)) 
+ "|" + upper(text('Tile Class')) 
+ "|" + if Qty then text(Qty) else "" end

Then in Effects → Trigger after create (or after update if you want to catch edits, too):

let k := this.EffectKey;
let p := this.Power;
if count(select Effects where Power = p and EffectKey = k) > 1 then
  alert("Duplicate effect for this power.");
  delete this
end

Result: you can add as many Effects as you want, but not the same tuple twice for the same Power.

4) Optional: human-readable summary

Add a Formula field on Effects:

// PrettySummary (Formula)
switch Action do
  case "Create" then "Create " 
  case "Transform" then "Transform "
  case "Destroy" then "Destroy "
  else text(Action) + " "
end
+ if Qty then text(Qty) + " " else "" end
+ lower(text('Tile Class')) + " "
+ if Color then "tiles to " + lower(text(Color)) + " " else "" end
+ "(" + lower(text(Selection)) + ")"

And on Powers, add a Formula to concatenate all child summaries:

// EffectsSummary on Powers (Formula)
concat(
  (select Effects where Power = this),
  "\n",
  PrettySummary
)

That gives each Power a compact, readable list of its variable effects.

CSV import notes

Edge cases & alternatives

This structure gives you exactly what you described: each Power holds a variable-length list of standardized effect descriptors, with clean de-duplication and easy display.